這是一個系列,咱們將其命名爲工具箱,若是你尚未看以前的文章:php
Android工具箱之文件目錄html
Android工具箱之理解app資源文件android
Android工具箱之Activity生命週期segmentfault
這幾天一直在思考一個問題,爲何國內的熱門博客和熱門教程都是好久以前的,例如我向學習EventBus,不管是鴻洋的博文仍是其餘論壇,幾乎清一色的OnEvent,或者好比我想學習Dagger2,文章數量更是少之又少,關鍵大量仍是Dagger1的內容。ide
基於此,外加上看到CodePath公司整合的Android資源正好符合實際需求,因此特地在sg開闢專欄,但願你們可以喜歡,在此申明下,由於工做量巨大,我很是有幸可以同@xixicat一塊兒翻譯這一專題,也懇請你們,如遇到任何翻譯錯誤,請指正,可評論中註明,也可電郵我,同時若是某位志趣相投人士有興趣參與翻譯,也可電郵我,我會進一步聯繫你:neuyuandaima@gmail.com。工具
廢話很少說,那麼咱們就開始吧。佈局
你有多少次在StackOverflow中尋找答案時,發現其答案居然是2年前的,你又有多少次搜出的博文是幾年前的舊文呢,我相信絕大部分的你都有這樣的經歷,因此咱們爲何不能利用社交讓咱們的的Android文檔佈滿每一個細節呢。
Context對象能夠獲取應用狀態的信息,其使得activitys和Fragments以及Services可以使用資源文件,圖片,主題,以及其餘的文件夾內容。其也能夠用於使用Android自帶服務,例如inflate,鍵盤,以及content providers。
不少狀況下,當你須要用到Context的時候,你確定只是簡單的利用當前activity的實例this。當你一個被activity建立的內部對象的時候,例如adapters裏或者fragments裏的時候,你須要將activity的實例傳給它們。而當你在activity以外,例如application或者service的時候,咱們須要利用application的context對象代替。
Intent intent = new Intent(context, MyActivity.class); startActivity(intent);
TextView textView = new TextView(context);
Contexts包含了如下信息:
設備的屏幕大小以及將dp,sp轉化爲px的尺寸。
style屬性
onClick屬性
咱們使用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());
在此就不一一列舉系統服務了,系統服務列表參見。
當主題被運用在應用層面,其也可被運用在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。
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; } }
爲了不內存泄露,不要在其生命週期之外持有該對象。檢查你的非主線程,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; }