1. 程式人生 > >瞭解Android四大元件

瞭解Android四大元件

Activity

用途:Activity是一個應用程式元件,其所有操作都與使用者密切相關,它提供一個螢幕,在此進行使用者互動從而完成某項任務。(是使用者操作的視覺化介面;它為使用者提供了一個完成操作指令的視窗)在一個android應用中,一個Activity通常就是一個單獨的螢幕,它上面可以顯示一些控制元件也可以監聽並處理使用者的事件做出響應。(可以通過setContentView(View)來顯示指定控制元件。)Activity之間通過Intent進行通訊。


四種基本狀態:

一、Active/Running
一個新 Activity 啟動入棧後,它顯示在螢幕最前端,處理是處於棧的最頂端(Activity棧頂),此時它處於可見並可和使用者互動的啟用狀態。
二、Paused


當 Activity失去焦點, 被一個新的非全屏的Activity 或者一個透明的Activity 被放置在棧頂,此時的狀態叫做暫停狀態(Paused)。此時它依然與視窗管理器保持連線,Activity依然保持活力(保持所有的狀態,成員資訊,和視窗管理器保持連線),但是在系統記憶體極端低下的時候將被強行終止掉。所以它仍然可見,但已經失去了焦點故不可與使用者進行互動。
三、 Stopped
如果一個Activity被另外的Activity完全覆蓋掉,叫做停止狀態(Stopped)。它依然保持所有狀態和成員資訊,但是它不再可見,所以它的視窗被隱藏,當系統記憶體需要被用在其他地方的時候,Stopped的Activity將被強行終止掉。
四、Killed

如果一個Activity是Paused或者Stopped狀態,系統可以將該Activity從記憶體中刪除,Android系統採用兩種方式進行刪除,要麼要求該Activity結束,要麼直接終止它的程序。當該Activity再次顯示給使用者時,它必須重新開始和重置前面的狀態。

狀態的轉換

這裡寫圖片描述

此處引用:
https://baike.baidu.com/item/activity/7304419?fr=aladdin


Activity的生命週期和相關操作

瞭解Activity棧

Android 是通過一種 Activity 棧的方式來管理 Activity 的,一個 Activity 的例項的狀態決定它在棧中的位置。處於前臺的 Activity 總是在棧的頂端,當前臺的 Activity 因為異常或其它原因被銷燬時,處於棧第二層的 Activity 將被啟用,上浮到棧頂。當新的 Activity 啟動入棧時,原 Activity 會被壓入到棧的第二層。一個 Activity 在棧中的位置變化反映了它在不同狀態間的轉換。Activity 的狀態與它在棧中的位置關係如下圖所示:
這裡寫圖片描述

生命週期

通過圖例初瞭解:
這裡寫圖片描述


七大生命週期函式
onCreate : 該方法是在Activity被建立時回撥,它是生命週期第一個呼叫的方法,我們在建立Activity時一般都需要重寫該方法,然後在該方法中做一些初始化的操作,如通過setContentView設定介面佈局的資源,初始化所需要的元件資訊等。

onStart : 此方法被回撥時表示Activity正在啟動,此時Activity已處於可見狀態,只是還沒有在前臺顯示,因此無法與使用者進行互動。可以簡單理解為Activity已顯示而我們無法看見擺了。

onResume : 當此方法回撥時,則說明Activity已在前臺可見,可與使用者互動了(處於前面所說的Active/Running形態),onResume方法與onStart的相同點是兩者都表示Activity可見,只不過onStart回撥時Activity還是後臺無法與使用者互動,而onResume則已顯示在前臺,可與使用者互動。當然從流程圖,我們也可以看出當Activity停止後(onPause方法和onStop方法被呼叫),重新回到前臺時也會呼叫onResume方法,因此我們也可以在onResume方法中初始化一些資源,比如重新初始化在onPause或者onStop方法中釋放的資源。

onPause : 此方法被回撥時則表示Activity正在停止(Paused形態),一般情況下onStop方法會緊接著被回撥。但通過流程圖我們還可以看到一種情況是onPause方法執行後直接執行了onResume方法,這屬於比較極端的現象了,這可能是使用者操作使當前Activity退居後臺後又迅速地再回到到當前的Activity,此時onResume方法就會被回撥。當然,在onPause方法中我們可以做一些資料儲存或者動畫停止或者資源回收的操作,但是不能太耗時,因為這可能會影響到新的Activity的顯示——onPause方法執行完成後,新Activity的onResume方法才會被執行。

onStop : 一般在onPause方法執行完成直接執行,表示Activity即將停止或者完全被覆蓋(Stopped形態),此時Activity不可見,僅在後臺執行。同樣地,在onStop方法可以做一些資源釋放的操作(不能太耗時)。

onRestart :表示Activity正在重新啟動,當Activity由不可見變為可見狀態時,該方法被回撥。這種情況一般是使用者打開了一個新的Activity時,當前的Activity就會被暫停(onPause和onStop被執行了),接著又回到當前Activity頁面時,onRestart方法就會被回撥。

onDestroy :此時Activity正在被銷燬,也是生命週期最後一個執行的方法,一般我們可以在此方法中做一些回收工作和最終的資源釋放。

七大生命週期函式引用自
https://blog.csdn.net/javazejian/article/details/51932554


程式碼例項

package com.example.kaixuan.myapplication;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar; import android.view.View;
import android.view.Menu; import android.view.MenuItem;

import android.util.Log;// 關鍵是這個包要被引用,其餘都是本機預設的。

public class MainActivity extends AppCompatActivity {

final String TAG = "--MainActivity--";
 // 定義Log.d(TAG, "-----onCreate-----");中的TAG;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.d(TAG, "-----onCreate-----");//在onCreate方法中插入此輸出語句。
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}
// 上面為本機預設程式碼,無需修改,下面是測試生命週期時方法的重寫。
@Override
public void onStart() {
    super.onStart();
    Log.d(TAG, "------onStart-------");//為了顯示給我們看。
}
@Override
public void onRestart() {
    super.onRestart();
    Log.d(TAG, "------onRestart-------");
}
@Override
public void onResume() {
    super.onResume();
    Log.d(TAG, "------onResume-------");
}
@Override
public void onPause() {
    super.onPause();
    Log.d(TAG, "------onPause-------");
}
@Override
public void onStop() {
    super.onStop();
    Log.d(TAG, "------onStop-------");
}
@Override
public void onDestroy() {
    super.onDestroy();
    Log.d(TAG, "------onDestroy-------");
} }

在MainActivity.java中編寫上述程式碼,按倒三角號在真機執行,真機成功執行此小程式。
這裡寫圖片描述

此時logcat的顯示:
這裡寫圖片描述

然後我按下主螢幕鍵,返回主螢幕,注意:此時此程式還在我的收集後臺執行,此時logcat的顯示:
這裡寫圖片描述

最後,我退出小程式,返回小程式佔用的記憶體空間,此時logcat的顯示:
這裡寫圖片描述


Service

Service簡介

一個Service是一個長期可以在後臺執行(當然不需要提供UI)的應用元件。其它元件可以啟動service,即使切換到另一個應用,該service仍然可以在後臺執行。另外,其它元件可以繫結一個service進行互動,甚至可以進行程序間通訊(interprocess communication,IPC)。例如,服務可以處理網路事務、播放音樂,執行檔案 I/O 或與內容提供程式互動,而所有這一切均可在後臺進行。

  • 正如activity一樣,service也必須在AndroidManifest.xml中進行註冊。
  • service不是單獨的程序,除非特別指定,否則它在應用程式的程序中。
  • service不是執行緒,但是它經常開啟一個執行緒處理任務。

一個service本質上可以分為兩種:
Started
即啟動方式的service。當一個元件通過startService()方法啟動service時,該service為”started”。一旦service啟動,則無論啟動它的元件是否被銷燬,該service都能獨立執行。通常,這種方式的service執行一些簡單的操作,且不帶有返回值。例如,它能夠下載/上傳檔案,當操作完成時,該service需要停止。
Bound
即繫結方式的service。當元件通過bindService()方法繫結一個service時,該service為繫結service。繫結service允許元件和繫結service進行互動,例如傳送請求,獲得結果,甚至跨程序通訊(IPC)。一個繫結service一旦被繫結,該service就會處於執行狀態。多個元件可以繫結一個service,但只有所有的元件都解綁時,該service才銷燬。

為了建立一個service,我們必須實現一個Service的子類(或Service的存在的子類)。通常,需要覆蓋一些父類的回撥方法,以此實現service生命週期的關鍵部分,同時如果需要的話,還要提供元件繫結它的機制(後面有詳細介紹繫結的過程)。我們應該覆蓋的主要方法如下:

onStartCommand() 
當一個元件通過startService()啟動一個service時,系統會回撥該方法。
一旦該方法執行,service就會在後臺無限制地執行。
因此,你必須負責對該service的停止(通過呼叫stopSelf()stopService())。
如果只是想繫結service,則不需要實現該方法。 
每次startService()都會執行該方法。
onBind() 
當另一個元件想要通過bindService()繫結service時,系統會回撥該方法。
該方法需要實現一個介面,通過返回IBinder物件實現使用者通訊。
若不需要繫結service則返回null即可,否則必須實現。
onCreate()service第一次被建立時,系統會呼叫該方法。
該方法在上述兩個方法之前呼叫,且只會執行一次,用來一次性的操作。
若service處於執行狀態,則系統不會回撥該方法。
onDestroy()service不再被使用,準備銷燬時,系統呼叫該方法銷燬service。
這是service最後被呼叫的方法,通常用於釋放資源。

當元件通過startService()方式啟動service時,系統就會回撥onStartCommand()方法,然後直到該service方法呼叫stopSelf()結束自己或另一個元件呼叫stopService()方法結束該service,它才會停止並被銷燬。
當一個元件通過bindService()方式建立service時(系統不會呼叫onStartCommand()),那麼只要該元件繫結它,它就一直處於執行狀態。一旦service被所有的元件解綁,那麼系統就會銷燬該service。

有關係統殺死Service的一些事
系統會在記憶體不足時強制殺死service,以保證當前獲得焦點的activity能夠獲得系統資源。若繫結service的activity處於執行狀態,則不太可能銷燬該service,同時若該service被宣告為前臺service(run in the foreground,startForegroud()設定為前臺service),則它最不可能被殺死。否則,started service執行時間越長,被銷燬的概率越大(執行時間越長所在後臺任務中的位置越低,越容易被銷燬)。因此,如果我們需要啟動service的話,最好要設定好如何通過系統重新啟動該service。如果系統銷燬了service,系統通過onStartCommand()方法的返回值判斷是否要重新啟動該service。

Service的生命週期

這裡寫圖片描述

下面為官方對於Service的描述

服務的整個生命週期從呼叫 onCreate() 開始起,到 onDestroy() 返回時結束。
與 Activity 類似,服務也在onCreate() 中完成初始設定,並在 onDestroy() 中釋放所有剩餘資源。例如,音樂播放服務可以在 onCreate()中建立用於播放音樂的執行緒,然後在 onDestroy() 中停止該執行緒
無論服務是通過 startService() 還是bindService() 建立,都會為所有服務呼叫 onCreate() 和 onDestroy() 方法。
服務的有效生命週期從呼叫 onStartCommand() 或 onBind() 方法開始。每種方法均有 Intent物件,該物件分別傳遞到 startService() 或 bindService()。
對於啟動服務,有效生命週期與整個生命週期同時結束(即便是在 onStartCommand()返回之後,服務仍然處於活動狀態)。
對於繫結服務,有效生命週期在 onUnbind() 返回時結束。

推薦一篇關於Service的全面且優秀解讀

Service的參考網站:https://blog.csdn.net/wangyongge85/article/details/46873203#commentBox
https://blog.csdn.net/javazejian/article/details/52709857


BroadcastReceiver

在Android系統中,廣播體現在方方面面,例如當開機完成後系統會產生一條廣播,接收到這條廣播就能實現開機啟動服務的功能;當網路狀態改變時系統會產生一條廣播,接收到這條廣播就能及時地做出提示和儲存資料等操作;當電池電量改變時,系統會產生一條廣播,接收到這條廣播就能在電量低時告知使用者及時儲存進度,等等。

1.同一app內部的同一組件內的訊息通訊(單個或多個執行緒之間);
2.同一app內部的不同元件之間的訊息通訊(單個程序);
3.同一app具有多個程序的不同元件之間的訊息通訊;
4.不同app之間的元件之間訊息通訊;
5.Android系統在特定情況下與App之間的訊息通訊。

其作用:監聽 / 接收 應用 App 發出的廣播訊息,並 做出響應

Android廣播的兩個角色: 廣播發送者 廣播接收者

具體實現流程要點粗略概括如下:

  1. 廣播接收者BroadcastReceiver通過Binder機制向AMS(Activity Manager Service)進行註冊;
  2. 廣播發送者通過binder機制向AMS傳送廣播;
  3. AMS查詢符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(一般情況下是Activity)相應的訊息迴圈佇列中;
  4. 訊息迴圈執行拿到此廣播,回撥BroadcastReceiver中的onReceive()方法。

廣播的兩種註冊方式

靜態註冊:

<receiver android:enabled=["true" | "false"] 
//此broadcastReceiver能否接收其他App的發出的廣播 
//預設值是由receiver中有無intent-filter決定的:
如果有intent-filter,預設值為true,否則為false 
android:exported=["true" | "false"]
android:icon="drawable resource" 
android:label="string resource" 
//繼承BroadcastReceiver子類的類名 
android:name=".mBroadcastReceiver" 
//具有相應許可權的廣播發送者傳送的廣播才能被此BroadcastReceiver所接收; 
android:permission="string" 
//BroadcastReceiver執行所處的程序 
//預設為app的程序,可以指定獨立的程序 
//注:Android四大基本元件都可以通過此屬性指定自己的獨立程序 
android:process="string" > 

//用於指定此廣播接收器將接收的廣播型別
 //本示例中給出的是用於接收網路狀態改變時發出的廣播 
 <intent-filter> 
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
  </intent-filter>
</receiver>

  • 註冊示例
<receiver 
//此廣播接收者類是mBroadcastReceiver 
android:name=".mBroadcastReceiver" > 
//用於接收網路狀態改變時發出的廣播 
<intent-filter>
 <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> 
 </intent-filter>
</receiver>

當此 App首次啟動時,系統會自動例項化mBroadcastReceiver類,並註冊到系統中。

動態註冊

// 選擇在Activity生命週期方法中的onResume()中註冊 
@Override protected void onResume(){ 
    super.onResume();

 // 1. 例項化BroadcastReceiver子類 & IntentFilter 
mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();

 // 2. 設定接收廣播的型別 
IntentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE); 

// 3. 動態註冊:呼叫Context的registerReceiver()方法 
registerReceiver(mBroadcastReceiver, intentFilter); 
}

 // 註冊廣播後,要在相應位置記得銷燬廣播 
 // 即在onPause() 中unregisterReceiver(mBroadcastReceiver)
// 當此Activity例項化時,會動態將MyBroadcastReceiver註冊到系統中 
// 當此Activity銷燬時,動態註冊的MyBroadcastReceiver將不再接收到相應的廣播。 

@Override protected void onPause() { 
super.onPause(); 
//銷燬在onResume()方法中的廣播
 unregisterReceiver(mBroadcastReceiver); 
    }
}

廣播發送及廣播型別

經常說”傳送廣播“和”接收“,表面上看廣播作為Android廣播機制中的實體,實際上這一實體本身是並不是以所謂的”廣播“物件存在的,而是以”意圖“(Intent)去表示。定義廣播的定義過程,實際就是相應廣播”意圖“的定義過程,然後通過廣播發送者將此”意圖“傳送出去。被相應的BroadcastReceiver接收後將會回撥onReceive()函式。

下段程式碼片段顯示的是一個普通廣播的定義過程,併發送出去。其中setAction(..)對應於BroadcastReceiver中的intentFilter中的action。

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.putExtra("name", "qqyumidi");
4 sendBroadcast(intent);

根據廣播的傳送方式,可以將其分為以下幾種型別:

1.Normal Broadcast:普通廣播
2.System Broadcast: 系統廣播
3.Ordered broadcast:有序廣播
4.Sticky Broadcast:粘性廣播(在 android 5.0/api 21中deprecated,不再推薦使用,相應的還有粘性有序廣播,同樣已經deprecated)
5.Local Broadcast:App應用內廣播

下面分別總結下各種型別的傳送方式及其特點。

1).Normal Broadcast:普通廣播
此處將普通廣播界定為:開發者自己定義的intent,以context.sendBroadcast_”AsUser”(intent,…)形式。
具體可以使用的方法有:
sendBroadcast(intent)
sendBroadcast(intent,receiverPermission)
endBroadcastAsUser(intent,userHandler)
sendBroadcastAsUser(intent,userHandler,receiverPermission)
普通廣播會被註冊了的相應的感興趣(intent-filter匹配)接收,且順序是無序的。如果傳送廣播時有相應的許可權要求,BroadCastReceiver如果想要接收此廣播,也需要有相應的許可權。

2).System Broadcast: 系統廣播
Android系統中內建了多個系統廣播,只要涉及到手機的基本操作,基本上都會發出相應的系統廣播。如:開啟啟動,網路狀態改變,拍照,螢幕關閉與開啟,點亮不足等等。每個系統廣播都具有特定的intent-filter,其中主要包括具體的action,系統廣播發出後,將被相應的BroadcastReceiver接收。系統廣播在系統內部當特定事件發生時,有系統自動發出。

3)Ordered broadcast:有序廣播
有序廣播的有序廣播中的“有序”是針對廣播接收者而言的,指的是傳送出去的廣播被BroadcastReceiver按照先後循序接收。有序廣播的定義過程與普通廣播無異,只是其的主要傳送方式變為:sendOrderedBroadcast(intent,
receiverPermission, …)。 對於有序廣播,其主要特點總結如下:
1>多個具當前已經註冊且有效的BroadcastReceiver接收有序廣播時,是按照先後順序接收的,先後順序判定標準遵循為:將當前系統中所有有效的動態註冊和靜態註冊的BroadcastReceiver按照priority屬性值從大到小排序,對於具有相同的priority的動態廣播和靜態廣播,動態廣播會排在前面。
2>先接收的BroadcastReceiver可以對此有序廣播進行截斷,使後面的BroadcastReceiver不再接收到此廣播,也可以對廣播進行修改,使後面的BroadcastReceiver接收到廣播後解析得到錯誤的引數值。當然,一般情況下,不建議對有序廣播進行此類操作,尤其是針對系統中的有序廣播。

4)Sticky Broadcast:粘性廣播(在 android 5.0/api
21中deprecated,不再推薦使用,相應的還有粘性有序廣播,同樣已經deprecated)。
既然已經deprecated,此處不再多做總結。

5)Local Broadcast:App應用內廣播(此處的App應用以App應用程序為界)
由前文闡述可知,Android中的廣播可以跨程序甚至跨App直接通訊,且註冊是exported對於有intent-filter的情況下預設值是true,由此將可能出現安全隱患如下:
1.其他App可能會針對性的發出與當前App intent-filter相匹配的廣播,由此導致當前App不斷接收到廣播並處理;
2.其他App可以註冊與當前App一致的intent-filter用於接收廣播,獲取廣播具體資訊。

無論哪種情形,這些安全隱患都確實是存在的。由此,最常見的增加安全性的方案是:

  1. 對於同一App內部發送和接收廣播,將exported屬性人為設定成false,使得非本App內部發出的此廣播不被接收;
  2. 在廣播發送和接收時,都增加上相應的permission,用於許可權驗證;
  3. 傳送廣播時,指定特定廣播接收器所在的包名,具體是通過intent.setPackage(packageName)指定在,這樣此廣播將只會傳送到此包中的App內與之相匹配的有效廣播接收器中。

App應用內廣播可以理解成一種區域性廣播的形式,廣播的傳送者和接收者都同屬於一個App。實際的業務需求中,App應用內廣播確實可能需要用到。同時,之所以使用應用內廣播時,而不是使用全域性廣播的形式,更多的考慮到的是Android廣播機制中的安全性問題。
相比於全域性廣播,App應用內廣播優勢體現在:
1.安全性更高;
2.更加高效。

BroadcastReceiver參考:
https://www.cnblogs.com/lwbqqyumidi/p/4168017.html
https://www.jianshu.com/p/ca3d87a4cdf3


ContentProvider

作用

程序間 進行資料互動 & 共享,即跨程序通訊

具體使用

統一資源識別符號(URI)

定義:Uniform Resource Identifier,即統一資源識別符號 作用:唯一標識 ContentProvider &其中的資料外界程序通過 URI 找到對應的ContentProvider & 其中的資料,再進行資料操作。

URI分為 系統預置 & 自定義,分別對應系統內建的資料(如通訊錄、日程表等等)和自定義資料庫

這裡寫圖片描述

MIME資料型別

作用:

指定某個副檔名的檔案用某種應用程式來開啟。如指定.html檔案採用text應用程式開啟、指定.pdf檔案採用flash應用程式開啟

  • ContentProvider根據 URI 返回MIME型別
  • 每種MIME型別 由2部分組成 = 型別 + 子型別(MIME型別是 一個 包含2部分的字串)

  • MIME型別有2種形式:

// 形式1:單條記錄  
vnd.android.cursor.item/自定義
// 形式2:多條記錄(集合)
vnd.android.cursor.dir/自定義 

// 注:
  // 1. vnd:表示父型別和子型別具有非標準的、特定的形式。
  // 2. 父型別已固定好(即不能更改),只能區別是單條還是多條記錄
  // 3. 子型別可自定義

ContentProvider類

  1. ContentProvider主要以 表格的形式 組織資料
  2. 同時也支援檔案資料,只是表格形式用得比較多
  3. 每個表格中包含多張表,每張表包含行 & 列,分別對應記錄 & 欄位
  4. 同資料庫

主要方法

<-- 4個核心方法 -->
  public Uri insert(Uri uri, ContentValues values) 
  // 外部程序向 ContentProvider 中新增資料

  public int delete(Uri uri, String selection, String[] selectionArgs) 
  // 外部程序 刪除 ContentProvider 中的資料

  public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  // 外部程序更新 ContentProvider 中的資料

  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  
  // 外部應用 獲取 ContentProvider 中的資料

// 注:
  // 1. 上述4個方法由外部程序回撥,並執行在ContentProvider程序的Binder執行緒池中(不是主執行緒)
 // 2. 存在多執行緒併發訪問,需要實現執行緒同步
   // a. 若ContentProvider的資料儲存方式是使用SQLite & 一個,則不需要,因為SQLite內部實現好了執行緒同步,若是多個SQLite則需要,因為SQL物件之間無法進行執行緒同步
  // b. 若ContentProvider的資料儲存方式是記憶體,則需要自己實現執行緒同步

<-- 2個其他方法 -->
public boolean onCreate() 
// ContentProvider建立後 或 開啟系統後其它程序第一次訪問該ContentProvider時 由系統進行呼叫
// 注:執行在ContentProvider程序的主執行緒,故不能做耗時操作

public String getType(Uri uri)
// 得到資料型別,即返回當前 Url 所代表資料的MIME型別

參考:
https://www.2cto.com/kf/201707/661749.html


打完收工!!