Android工具箱之Context解析
這幾天一直在思考一個問題,為什麼國內的熱門部落格和熱門教程都是很久之前的,例如我向學習EventBus,不論是鴻洋的博文還是其他論壇,幾乎清一色的OnEvent,或者比如我想學習Dagger2,文章數量更是少之又少,關鍵大量還是Dagger1的內容。
基於此,外加上看到CodePath公司整合的Android資源正好符合實際需求,所以特意在sg開闢專欄,希望大家能夠喜歡,在此申明下,因為工作量巨大,我非常有幸能夠同@xixicat一起翻譯這一專題,也懇請大家,如遇到任何翻譯錯誤,請指正,可評論中註明,也可電郵我,同時如果某位志趣相投人士有興趣參與翻譯,也可電郵我,我會進一步聯絡你:neuyuandaima@gmail.com。
廢話不多說,那麼我們就開始吧。
動機
你有多少次在StackOverflow中尋找答案時,發現其答案竟然是2年前的,你又有多少次搜出的博文是幾年前的舊文呢,我相信絕大部分的你都有這樣的經歷,所以我們為什麼不能利用社交讓我們的的Android文件佈滿每個細節呢。
初篇之Context初探
概述
Context物件可以獲取應用狀態的資訊,其使得activitys和Fragments以及Services能夠使用資原始檔,圖片,主題,以及其他的資料夾內容。其也可以用於使用Android自帶服務,例如inflate,鍵盤,以及content providers。
很多情況下,當你需要用到Context的時候,你肯定只是簡單的利用當前activity的例項this。當你一個被activity建立的內部物件的時候,例如adapters裡或者fragments裡的時候,你需要將activity的例項傳給它們。而當你在activity之外,例如application或者service的時候,我們需要利用application的context物件代替。
Contex的用途
明確地啟動一個元件,例如activity或者service
Intent intent = new Intent(context, MyActivity.class); startActivity(intent);
建立檢視
TextView textView = new TextView(context);
Contexts包含了以下資訊:
- 裝置的螢幕大小以及將dp,sp轉化為px的尺寸。
- style屬性
- onClick屬性
inflate xml佈局檔案
我們使用context來獲得LayoutInflater,其可以在記憶體中inflate xml佈局檔案
LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.my_layout, parent);
傳送本地廣播
我們使用context來獲得LocalBroadcastManager,其可以傳送或者註冊廣播接收。
Intent broadcastIntent = new Intent("custom-action"); LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
獲取系統服務
例如當你需要傳送通知,你需要NotificationManager。
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); int notificationId = 1; // Context is required to construct RemoteViews Notification.Builder builder = new Notification.Builder(context).setContentTitle("custom title"); notificationManager.notify(notificationId, builder.build());
在此就不一一列舉系統服務了, 系統服務列表 參見。
應用級別的Context和Activity級別的Context
當主題被運用在應用層面,其也可被運用在activity層面,比如當應用層面定義了一些主題,activity可以將其覆蓋。
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/MyCustomTheme">
大部分檢視需要傳入activity級別的Context物件,這樣其才能獲取主題,styles,dimensions等屬性。如果某個控制元件沒有使用theme,其預設使用了應用的主題。
在大部分情況下,你需要使用activity級別的Context。通常,關鍵字this代表著一個類的例項,其可被用於activity中的Context傳遞。例如:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toast.makeText(this, "hello", Toast.LENGTH_SHORT).show(); } }
匿名方法
當我們使用了匿名內部類的適合,例如實現監聽,this關鍵字的使用:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { TextView tvTest = (TextView) findViewById(R.id.abc); tvTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(MainActivity.this, "hello", Toast.LENGTH_SHORT).show(); } }); } }
介面卡
陣列介面卡
當你為listview定義介面卡的適合,getContext()方法被經常使用,其用來例項化xml佈局。
if (convertView == null) { convertView = LayoutInflater .from(getContext()) .inflate(R.layout.item_user, parent, false); }
注意:當你傳入的是應用級別的context,你會發現themes/styles屬性將不會被應用,所以確保你在這裡傳入的是Activity級別的context。
RecyclerView介面卡
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.ViewHolder> { @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater .from(parent.getContext()) .inflate(itemLayout, parent, false); return new ViewHolder(v); } @Override public void onBindViewHolder(ViewHolder viewHolder, int i) { // If a context is needed, it can be retrieved // from the ViewHolder's root view. Context context = viewHolder.itemView.getContext(); // Dynamically add a view using the context provided. if(i == 0) { TextView tvMessage = new TextView(context); tvMessage.setText("Only displayed for the first item.") viewHolder.customViewGroup.addView(tvMessage); } } public static class ViewHolder extends RecyclerView.ViewHolder { public FrameLayout customViewGroup; public ViewHolder(view imageView) { super(imageView); // Perform other view lookups. customViewGroup = (FrameLayout) imageView.findById(R.id.customViewGroup); } } }
可以看到,ArrayAdapter需要在其構造器裡面傳入context,RecyclerView.Adapter不需要。
RecyclerView通常將其作為父檢視傳給RecyclerView.Adapter.onCreateViewHolder()。
如果在onCreateViewHolder()方法的外面,你需要用到context,你也可以使用ViewHolder,例如viewHolder.itemView.getContext()。itemView是一個公有,非空,final型別的成員變數。
避免記憶體洩露
應用級別的context通常在單例中使用,例如一個常用的管理類,其管理Context物件來獲取系統服務,但是其不能同時被多個activity獲取。由於維護一個activity級別的context引用會導致記憶體洩露,所以你需要使用application級別的context替代。
在下面這個例子中,如果context是activity級別或者service級別,當其被destroy,其實際不會被gc, 因為CustomManager類擁有了其static應用。
pubic class CustomManager { private static CustomManager sInstance; public static CustomManager getInstance(Context context) { if (sInstance == null) { // This class will hold a reference to the context // until it's unloaded. The context could be an Activity or Service. sInstance = new CustomManager(context); } return sInstance; } private Context mContext; private CustomManager(Context context) { mContext = context; } }
適當地儲存context:利用應用級別context
為了避免記憶體洩露,不要在其生命週期以外持有該物件。檢查你的非主執行緒,pending handlers或者內部類是否持有context物件。
儲存應用級別的context的最好的辦法是CustomManager.getInstance(),其為單例,生命週期為整個應用的程式。
public static CustomManager getInstance(Context context) { if (sInstance == null) { // When storing a reference to a context, use the application context. // Never store the context itself, which could be a component. sInstance = new CustomManager(context.getApplicationContext()); } return sInstance; }
參考
相關文章
- Android全面解析之Context機制AndroidContext
- Android開發 - Context解析AndroidContext
- Android之Context底層原理AndroidContext
- Android Context完全解析,你所不知道的Context的各種細節AndroidContext
- Android之Activity全面解析Android
- Android - 認識ContextAndroidContext
- 星雲 Android 開發工具箱Android
- Android之XML檔案解析AndroidXML
- Android基礎之Activity全解析Android
- Android全面解析之Window機制Android
- Android中Context、Activity、ApplicatioAndroidContextAPP
- Android中Context樣式分析AndroidContext
- Android中Context用法詳解AndroidContext
- Android中的Context詳解AndroidContext
- Android全面解析之Activity生命週期Android
- Android 原始碼解析 之 setContentViewAndroid原始碼View
- 甩手工具箱:淘寶最新規則變動解析
- 系統管理的工具箱之: iftop
- 《Lua-in-ConTeXt》08:引數列表解析Context
- Spring IoC Context啟動過程解析SpringContext
- 深入理解 Golang 之 contextGolangContext
- React狀態管理之ContextReactContext
- go context剖析之使用技巧GoContext
- Oracle全文檢索之ContextOracleContext
- Android技術分享| Context淺析AndroidContext
- Android系統原始碼分析--ContextAndroid原始碼Context
- Android Context 到底是什麼?AndroidContext
- Android -- Options Menu,Context Menu,Popup MenuAndroidContext
- Android基礎之json資料解析AndroidJSON
- Android之json複雜資料解析AndroidJSON
- 深入理解Javascript之Execution ContextJavaScriptContext
- Android中Context的詳細介紹AndroidContext
- Android元件管理框架:Android應用上下文ContextAndroid元件框架Context
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- Android控制之垂直滾動廣告條ViewFLipper解析AndroidView
- Android okHttp網路請求之Json解析AndroidHTTPJSON
- Android學習系列(36)--App除錯記憶體洩露之Context篇(上)AndroidAPP除錯記憶體洩露Context
- Android學習系列(37)--App除錯記憶體洩露之Context篇(下)AndroidAPP除錯記憶體洩露Context