这是一个系列,咱们将其命名为工具箱,若是你尚未看以前的文章: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; }