亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JAVA中Context的詳細介紹和實例分析

 更新時間:2020年07月14日 10:33:12   作者:擱淺...  
這篇文章主要介紹了JAVA中Context的詳細介紹和實例分析,Context是維持android各組件能夠正常工作的一個核心功能類。如果感興趣來學習一下

最熟悉的陌生人——Context

剛剛學android或者js等,都會看見這個頻繁的字眼——Context。
意為”上下文“。

本文主要記述,Context到底是什么、如何理解Context、一個APP可以有幾個Context、Context能干啥、Context的作用域、獲取Context、全局獲取Context技巧。

思考:

Java:萬物皆對象。Flutter:萬物皆組件。
俗語:”沒對象嗎?自己new一個啊~“
既然大多數情況可以new一個實例,那么,我們在android中的Activity實例怎么獲取呢?Activity.instance可以獲取activity。既然Activity也大致歸屬于一個類,那么可不可以用 Activity activity=new Activity(); 呢?安卓不像Java程序一樣,隨便創(chuàng)建一個類,寫個main()方法就能運行,**Android應用模型是基于組件的應用設計模式,組件的運行要有一個完整的Android工程環(huán)境。在這個環(huán)境下,Activity、Service等系統(tǒng)組件才能正常工作,而這些組件不能采用普通的java對象創(chuàng)建方式,new一下是不能創(chuàng)建實例的,而是要有它們各自的上下文環(huán)境,也就是Context.
所以說,Context是維持android各組件能夠正常工作的一個核心功能類。

what 's Context:

(本圖為沙拉查詞給出的中文翻譯)

有點晦澀難懂。但在程序中,我們可理解為當前對象在程序中所處的一個環(huán)境,一個與系統(tǒng)交互的過程。 比如QQ和你們自己的女朋友聊天時(沒有grilfriend的可自己跳過舉例),此時的context是指的聊天界面以及相關的數據請求與傳輸,Context在加載資源、啟動Activity、獲取系統(tǒng)服務、創(chuàng)建View等操作都要參與。

所以,一個Activity就是一個Context(getActivity()==getContext),一個Service也是一個Context。Android把場景抽象為Context類,用戶和操作系統(tǒng)的每一次交互都是一個場景,比如:打電話、發(fā)短信等,都有activity,還有一些我們肉眼看不見的后臺服務。一個應用程序可以認為是一個工作環(huán)境,用戶在這個環(huán)境中切換到不同的場景,這就像服務員,客戶可能是外賣小哥、也可能是農民工等,這些就是不同的場景,而服務員就是一個應用程序。

How to understand the ‘Context':

Context理解為”上下文“/”場景“,可能還是很抽象。那么我們可以做一個比喻:
一個APP是仙劍奇?zhèn)b傳3電視劇,Activity、Service、BroadcastReceiver、ContentProvider這四大組件就是電視劇的主角。它們是導演(系統(tǒng))一開始就確定好試鏡成功的人。換言之, 不是我們每個人都能被導演認可的。有了演員,就要有鏡頭啊,這個鏡頭便是(Context)。通過鏡頭,我們才能看見帥氣 的胡歌。演員們都是在鏡頭(Context環(huán)境)下表演的。那么Button這些組件子類型就是配角,它們沒有那么重要,隨便一個組件都能參與演出(即隨便new 一個實例),但是它們也需要參與鏡頭,不然一部戲只有主角多沒意思,魔尊重樓還是要的,魔尊也要露面(工作在Context環(huán)境下),所以可以用代碼new Button();或者xml布局定義一個button。

打開AndroidStudio,輸入Context,然后ctrl+鼠標左鍵追朔其源碼(看源碼一般都先看注釋便于理解):import android.content.Context;

看注釋,TMD,是English,那么筆者這里就用小學生英語水平來翻譯一哈哈:
Context提供了關于應用環(huán)境全局信息的接口。它是一個abstract類,它的執(zhí)行被Android系統(tǒng)提供,允許獲取以應用為特征的資源和類型,是一個統(tǒng)領一些資源APP環(huán)境變量等的上下文。通過它可以獲取應用程序的資源和類(包括應用級別操作,如啟動Activity,發(fā)廣播,接收intent等)。abstract會有它的實現(xiàn)類。在源碼中,我們可以通過AndroidStudio去查看它的子類,得到以下關系:
它有2個具體實現(xiàn)子類:ContextImpl、ContextWrapper。

  • 其中,ContextWrapper類,只是一個包裝類,其構造函數中必須包含一個Context引用,同時它提供了attachBaseContext()用于給ContextWrapper對象中指定真正的Context對象,調用它的方法都會被轉向其所包含的真正的Context對象。
  • ContextThemeWrapper類其內部包含了與主題相關的接口。主題就是清單文件中android:theme為Application或Activity元素指定的主題。(Activity才需要主題,Serviceu不需要,因為服務是沒有界面的后臺場景,所以服務直接繼承ContextWrapper。Application同理。)而Contextlmpl類則是真正實現(xiàn)了Context中的所有函數,應用程序中所調用的各種Context類的方法,其實現(xiàn)均來自這個類。
  • 換言之:Context的2個實現(xiàn)子類分工的,其中ContextImpl是Context的具體是實現(xiàn)類,而ContextWrapper則是Context的包裝類。Activity、Application、Service都繼承自ContextWrapper(Activity繼承自ContextWrapper的子類ContextThemeWrapper),但它們的初始化過程中都會創(chuàng)建ContextImpl對象,由ContextImpl實現(xiàn)Context中的方法。

How much has Context in a App:

關鍵在于對COntext的理解。從上面提到的實現(xiàn)子類可以看出,在APP中,Context的具體實現(xiàn)子類是Acitivity、Service、Applicaiton。所以Context's number=Activity's number + Service's number+1(1個APP只有一個Application)。為啥不是4大組件,上面不是說四大組件也是主角嗎?看看BroadcastReceiver和ContentProvider的源碼可以知道它們并不是Context的子類,它們持有的Context都是其他地方傳遞過去的(比如我們發(fā)送廣播intent中的context就是外部傳遞過來的),所以不計數它們。

Context's method:

Context哪里會用到它。剛開始了解Android的時候不知道它是個啥玩意兒,但是久了發(fā)現(xiàn)有些地方就不得不傳這個參數。
比如Toast、啟動Activity、啟動Service、發(fā)送廣播、操作數據庫等等都需要傳Context參數,具體例子就不說了。詳細可以看后文將提到的如何獲取它。

Context's 作用域

不是隨便獲取一個Context實例就可以的,它的使用有一些規(guī)則和限制。因為Context的具體實例是由ContextImpl類去實現(xiàn)的,因此,Activity、Service、Application3種類型的Context都是等價的。但是,需要注意的是,,有些場景,比如啟動Activity、彈出Dialog等。為了安全,Android不允許Activity或者Dialog憑空出現(xiàn),一個Activity的啟動肯定是由另一個Activity負責的,也就是以此形成的返回棧(具體可以看看任主席的《Android開發(fā)藝術探索》)而Dialog則必須是在一個Activity上彈出(系統(tǒng)Alert類型的Dialog除外),這種情況下, 我們只能用Activity類型的Context,否則報錯。

Context作用域 Application Activity Service
Show a Dialog No Yes No
Start an Activity 不推薦 Yes 不推薦
Layout Inflation 不推薦 Yes 不推薦
Start a Service Yes Yes Yes
Send a Broadcast Yes Yes Yes
Register Broadcast Receiver Yes Yes Yes
Load Resource Values Yes Yes Yes

Activity繼承自ContextThemeWrapper,而Application和Service繼承ContextWrapper,所以ContextThemeWrapper在ContextWrapper的基礎上作了一些操作,使得Activity更加厲害。

關于表格中提到的Application和Service不推薦的2種情況:

1.如果用ApplicationContext去啟動一個LaunchMode為standard的Activity的時候會報錯:androud,util.AndroidRuntimeException:Calling startActivity from outside of an Activity context require the FLAG_ACTIVITY_NEW_TASK flag。Is this really what you want?
翻譯一下,并了解這個FLAG的都知道,此時的非Activity類型的Context并沒有所謂的返回棧,因此帶啟動的Activity就找不到棧。它還給我們明確之處了FLAG的解決辦法,這樣啟動的時候就為它創(chuàng)建一個新的任務棧,而此時Activity是以Single Task模式啟動的。所以這種用Application Context啟動Activity的方式不推薦,Service同理。

2.在Application和Service中去layout inflate也是合法的,但是會使用系統(tǒng)默認的主題樣式,如果自定義了某些樣式可能不會被使用,所以也不推薦。

注:和UI相關的,都應該使用Activity Context來處理。其他的一些操作,Service、Activity、Application等實例都是可以的。同時要注意Context的引用持有,防止內存泄漏??稍诒讳N毀的時候,置Context為null。

How to get the ‘Context':

常用4種方法獲取Context對象:

1.View.getContext():返回當前View對象的Context對象。通常是當前正在展示的Activity對象。
1.Activity,getApplicationContext()[后文會詳細介紹這個方法]:獲取當前Activity所在應用進程的Context對象,通常我們使用3.Context對象時,要優(yōu)先考慮這個全局的進程Context。
ContextWrapper.getBaseContext():用來獲取一個ContextWrapper進行裝飾之前的Context。實際開發(fā)很少用,也不建議使用。
4.Activity.this:返回當前Activity的實例,如果的UI控件需要使用Activity作為Context對象,但默認的Toast實際上使用的ApplicationContext也可以。
實現(xiàn)View.OnClick監(jiān)聽方法中,寫Toast,不要用this,因為this,在onClick(View view)指的是view對象而不是Activity實例,所以在這個方法中,應該使用”當前的Activity名.this“,這是入門者比較容易混淆的地方。

getApplication()和getApplicationContext():
獲取當前Application對象用getApplicationContext.但是getApplication又是什么。
我們可以自己寫代碼打印一下:

Application app=(Application)getApplication();
Log.e(TAG,"getApplication is "+app);
Context context=getApplicationContext();
Log.e(TAG,"getApplicationContext is "+ context);

運行后看logcat,效果圖就不貼了(電腦卡)。從打印結果可以看出它們2個的內存地址是相同的,即它們是同一個對象。 因為Application本來就是一個Context,那么這里獲取的getApplicationContext()自然也是Application本身的實例了。那這2個相同方法存在的意義是啥?(雙胞胎?)實際上這2個方法在作用域上有比較大的區(qū)別。 getApplication()一看就知道是用來獲取Application實例的(道理可以聯(lián)想getActivity())。但getApplication()只有在Activity和Service中才能調用的到。 對于比如BroadcastReceiver等中也想要獲取Application實例,這時就需要getApplicationContext()方法。

//繼承BroadcastReceiver并重寫onReceive()方法
@Override
public void onReceive(Context context.Intent intent){
 Application app=(Application)context.getApplicationContext();
}

內存泄漏之Context:

我們經常會遇到內存泄漏,比如Activity銷毀了,但是Context還持有該Activity的引用,造成了內存泄漏。(經常遇到)

2種典型的錯誤引用方式:

1.錯誤的單例模式:

public class Singleton{
 private static Singleton instancel
 private Context context;
 private Singleton(Context context){
 this.context=context;
 }
 public static Singleton getInstance(Context context){
 if(instance == null ){
 instance=new Singleton(context);
 }
 return instance;
 }
}

熟悉單例模式的都知道,這是一個非線程安全的單例模式,instance作為靜態(tài)對象,其生命周期要長于普通的對象(單例直到APP退出后臺才銷毀),其中也包含了Activity。比如Activity A去getInstance()得到instance對象,傳入this,常駐內存的Singleton保存了我們傳入的A對象,并一直持有,即使Activity被銷毀掉,但因為它的引用還存在于一個Singleton中,就不可能被GC掉,這樣就導致了內存泄漏。比如典型的數據庫操作,存儲數據,需要重復的去索取數據,用單例保持數據和拿到Activity持有context引用,因為單例可以看作是上帝,它幫我們保存數據。所以即使Activity被finish掉,還有它的引用在Singleton中。

View持有Activity引用:

public class MainActivity extend Activity{
 private static Drawable mDrawable; 
 @Override
 protected void onCreate(Bundle saveInstanceState){
 super.onCreate();
 setContentView(R.layout.activity_main);
 ImageView imageview=new ImageView(this);//通過代碼動態(tài)的創(chuàng)建組件,而不是傳統(tǒng)的xml配置組件,這里的ImageView持有當前Activity的引用。
 mDrawable=getResources().getDrawable(R.drawable.ic_launcher);
 imageview.setImageDrawable(mDrawable);
 }
}

上述代碼中,有一個static的Drawable對象。當ImageView設置這個Drawable的時候,ImageView保存了這個mDrawable的引用,而ImageView初始化的時候又傳入了this,此處的this是指MainActivity的context。因為被static修飾的mDrawable是常駐內存的(比類還要早加載)。MainActivity是它的間接引用了,當MainActivity被銷毀的時候,也不能被GC掉,就造成了內存泄漏。

How to get the context in the whole :

大量的地方都需要使用Context,我們常常會因為不知道怎么得到這個Context而苦惱。那么,全局獲取Context無疑是最好的解決方案。
很多時候,我們也不是經常為得不到Context而發(fā)愁,畢竟我們很多的操作都是在活動中進行的,而活動本身就是一個Context對象。但APP架構復雜后,很多邏輯代碼都脫離了Activity類,此時又需要使用Context,所以我們需要采取全局獲取Context的方法。
舉例, 我們平常經常會寫網絡工具類,比如下面的這些代碼:

public calss HttpUtil{
 public static void sendHttpRequest(final String address,final HttpCallbackListener listener){
 new Thread(new Runnable()){
 @Override 
 public void run(){
 HttpURLConnection connection=null;
 try{
 URL url =new URL(address);
 connection=(HttpURLConnection)url.openConnection();
 connection.setRequestMethod("GET");
 connection.setConnectTimeout(8000);
 connection.setReadTimeout(8000);
 connection.setDoInput(true);
 connection.setDoOutput(true);
 InputStream in =connection.getInputStream();
 BufferedReader reader=new BufferedReader(new InputStreamReader(in));
 StringBuilder response=new StringBuilder();
 String line;
 while((line=reader.readLine())!=nulll){
 response.append(line);
 }
 if(listener!=null){
 //回調onFinish()
 listener.onFinish(response.toString);
 }
 }catch(Execption e){
 if(listener!=null){
 //回調onError()
 listener.onError(e);
 }
 }finally{
 if(connection!=null){
 connection.disconnect();
 }
 }
 }}.start();
 }
}

上述代碼中使用sendHttpRequest()方法來發(fā)送HTTP請求顯然沒問題。并且還可以在回調方法中處理服務器返回的數據。但是這個方法還可以被優(yōu)化。當檢測不到網絡存在的時候就給用戶一個Toast,并不再執(zhí)行后面的代碼。問題來了,Toast需要一個Context參數,但是在本來沒有可以傳遞的Context對象。。。
一般思路:在方法中添加一個COntext參數:

 public static void sendHttpRequest(final String address,final HttpCallbackListener listener,final Context context){
 if(!isNetWorkAvailable()){
 Toast.makeText(context,……);
 ……
 }
 ……

看似可以,但是有點甩鍋。我們將獲取Context的任務轉移到了sendHttpRequest()方法的調用方。至于調用方能不能得到COntext對象就不是我們要考慮的問題了。

甩鍋不一定是通用的解決方案。于是這里介紹哈如何獲取全局Context的步驟:,通過它在項目的任何地方都能輕松的獲取到Context。:

Android提供了一個Application類,每當APP啟動的時候,系統(tǒng)就會自動將這個類進行初始化。我們可以定制一個自己的Application類,以便管理程序內一些全局的狀態(tài)信息,比如說全局Context。
定制一個自己的Application并不復雜,首先, 需要創(chuàng)建一個MyApplication類繼承自系統(tǒng)的Application:

public calss MyApplication extends Application{
 private static Context context;
 @Overrride
 public void onCreate(){
 context=getApplicationContext();
 }
 public static Context getContext(){
 return context;
 }
}

代碼很簡單,容易理解。重寫了父類的onCreate()方法,并通過調用getApplicationContext()方法得到一個應用程序級別的Context,然后又提供了一個靜態(tài)的getContext()方法,在這里將剛才獲取到的COntext進行返回。

接下來,我們需要告訴系統(tǒng),當程序啟動的時候應該初始化MyApplication類,而不是系統(tǒng)默認的Application類。這一步需要在清單文件里面實現(xiàn),找到清單文件的<application>標簽下進行指定就可以了:

<manifest ……
……>
<application 
 android :name="com.example.myContext.MyApplication" //這里輸入.MyApplication也可以,或者輸入MyApplication根據AS提示自動補全包名
 ..>
</application>

注意:這里一定要加上完整的包名,不然系統(tǒng)將無法找到這個類。

以上就是實現(xiàn)了一種全局獲取Context的機制,在這個項目的任何地方使用Context,只需要調用MyApplication.getContext()就可以了。

關于自定義Application和LitePal配置沖突的問題:

自定義需要在清單文件寫出android.name="……"。而為了讓LitePal可以正常工作,也需要在清單文件下,配置:

android:name="org.litepal.LitePalApplication"

道理也是一樣的,這樣配置后,LitePal就能在內部自動獲取到Context了。
問題:當都已經配置過自定義的Application怎么辦?豈不是和LitePalApplication沖突了?
解答:任何一個項目都只能配置一個Application. 對于這種情況,LitePalApplication給出了很簡單的解決方案,在自定義的Application中去調用LitePal的初始化方法就可以了:

public calss MyApplication extends Application{
 private static Context context;
 @Overrride
 public void onCreate(){
 context=getApplicationContext();
 LitePalApplication.initialize(context);
 }
 public static Context getContext(){
 return context;
 }
}

這種寫法就相當于我們把全局Context對象通過參數傳遞給了LitePal,效果和在清單文件配置LitePalApplication是一樣的。

總結,如何在程序中正確的使用Context:

一般Context造成的內存泄漏,幾乎都是當Context銷毀的時候,因為被引用導致銷毀失敗。而Application的Context對象可以簡單的理解為伴隨著進程存在的(它的生命周期也很長,畢竟APP加載的時候先加載Application,我們可以自定義Application然后繼承系統(tǒng)的Application)。
正確使用:

當Applicatin的Context能搞定的情況下,并且生命周期長的對象,優(yōu)先使用Application的Context;
不要讓生命周期長于Activity的對象持有Activity的引用。
盡量不要在Activity中使用非靜態(tài)內部類。非靜態(tài)內部類會隱式持有外部類實例的引用。如果使用靜態(tài)內部類,將外部實例引用作為弱引用持有。

獲取全局context的另一種思路:

ActivityThread是主進程的入口,它的currentApplication返回值是application.

import android.app.Application;

import java.lang.reflect.InvocationTargetException;

/**
 * 這種方式獲取全局的Application 是一種拓展思路。
 * <p>
 * 對于組件化項目,不可能把項目實際的Application下沉到Base,而且各個module也不需要知道Application真實名字
 * <p>
 * 這種一次反射就能獲取全局Application對象的方式相比于在Application#OnCreate保存一份的方式顯示更加通用了
 */
public class AppGlobals {
 private static Application sApplication;

 public static Application getApplication() {
 if (sApplication == null) {
 try {
 sApplication = (Application) Class.forName("android.app.ActivityThread")
 .getMethod("currentApplication")
 .invoke(null, (Object[]) null);
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 } catch (InvocationTargetException e) {
 e.printStackTrace();
 } catch (NoSuchMethodException e) {
 e.printStackTrace();
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 }
 }
 return sApplication;
 }
}

到此這篇關于Context的詳細介紹和實例分析的文章就介紹到這了,更多相關Context的詳細介紹內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Mybatis Plus 字段為空值時執(zhí)行更新方法未更新解決方案

    Mybatis Plus 字段為空值時執(zhí)行更新方法未更新解決方案

    這篇文章主要介紹了Mybatis Plus 字段為空值時執(zhí)行更新方法未更新解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • Java調用Python腳本傳遞數據并返回計算結果

    Java調用Python腳本傳遞數據并返回計算結果

    實際工程項目中可能會用到Java和python兩種語言結合進行,這樣就會涉及到一個問題,Java如何調用Python腳本,感興趣的可以了解一下
    2021-05-05
  • IDEA創(chuàng)建Servlet程序的兩種實現(xiàn)方法

    IDEA創(chuàng)建Servlet程序的兩種實現(xiàn)方法

    Servlet是JavaWeb應用程序中不可或缺的組件之一,本文主要介紹了IDEA創(chuàng)建Servlet程序的兩種實現(xiàn)方法,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • maven中更改jdk版本的方法實現(xiàn)

    maven中更改jdk版本的方法實現(xiàn)

    本文主要介紹了maven中更改jdk版本的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • Mybatis-Plus打印sql日志兩種方式

    Mybatis-Plus打印sql日志兩種方式

    這篇文章主要給大家介紹了關于Mybatis-Plus打印sql日志兩種方式,Mybatis-plus是MyBatis增強工具包,用于簡化CRUD操作,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • SpringData JPA快速上手之關聯(lián)查詢及JPQL語句書寫詳解

    SpringData JPA快速上手之關聯(lián)查詢及JPQL語句書寫詳解

    JPA都有SpringBoot的官方直接提供的starter,而Mybatis沒有,直到SpringBoot 3才開始加入到官方模版中,這篇文章主要介紹了SpringData JPA快速上手,關聯(lián)查詢,JPQL語句書寫的相關知識,感興趣的朋友一起看看吧
    2023-09-09
  • Java instanceof關鍵字的的進一步理解

    Java instanceof關鍵字的的進一步理解

    這篇文章主要介紹了Java instanceof關鍵字的的進一步理解,本文用一些實例講解了instanceof操作符的一些知識,需要的朋友可以參考下
    2015-03-03
  • 詳解Java中的mapstruct插件使用

    詳解Java中的mapstruct插件使用

    mapstruct 的插件是專門用來處理 domin 實體類與 model 類的屬性映射的,我們只需定義 mapper 接口,mapstruct 在編譯的時候就會自動的幫我們實現(xiàn)這個映射接口,避免了麻煩復雜的映射實現(xiàn),對Java?mapstruct使用相關知識感興趣的朋友一起看看吧
    2022-04-04
  • 關于Spring注解@Async引發(fā)其他注解失效的解決

    關于Spring注解@Async引發(fā)其他注解失效的解決

    這篇文章主要介紹了關于Spring注解@Async引發(fā)其他注解失效的解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • RocketMQ消息隊列實現(xiàn)隨機消息發(fā)送當做七夕禮物

    RocketMQ消息隊列實現(xiàn)隨機消息發(fā)送當做七夕禮物

    這篇文章主要為大家介紹了RocketMQ消息隊列實現(xiàn)隨機消息發(fā)送當做七夕禮物,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08

最新評論