如何使用Context

原文地址:https://github.com/codepath/android_guides/wiki/Using-Context

概述

总的来说,Context为使用者提供了一种获取应用状态相关信息的途径。.有了它,Activities,Fragments和Services就可以取得资源文件,images,themes/styles以及外部目录的位置。同时,它也提供了Android内建服务的获取方式,这些服务包括布局填充,键盘以及content providers的获取。

在很多时候,我们都需要用到Context。一般情况下,我们都只需要传递一个当前activity的实例。当我们处于activity内部,操作的是由其创建的对象,比如adapters或者fragments时,我们需要把activity的实例传给这些对象。当我们处在activity外部,如在application或者service中,我们则可以使用"application"。


Context到底用来做什么呢?

下面是使用Context的几个具体例子。


显式的启动组件

// Provide context if MyActivity is an internal activity.
Intent intent = new Intent(context, MyActivity.class);
startActivity(intent);
当显式启动一个组件时,需要提供两类信息:

1.包名,确定了包含该组件的application

2.该组件完整的Java类名

如果启动的是一个内部组件,则包名可以通过context.getPackageName()来获取。


创建一个View

TextView textView = new TextView(context);
Contexts包含了Views需要的几种信息:

1.设备的屏幕尺寸,以便将dp,sp转化为pixels

2.样式属性

3.onClick属性所持有的acitivity引用


填充XML布局文件

在使用LayoutInflater时,我们需要使用context将XML布局填充到内存:

// A context is required when creating views.
LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.my_layout, parent);

发送local broadcast

在我们需要发送广播或者注册receiver时,我们通过context来获得LocalBroadcastManager:

// The context contains a reference to the main Looper, 
// which manages the queue for the application's main thread.
Intent broadcastIntent = new Intent("custom-action");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);


调用系统服务

在应用发送通知时,需要使用到NotificationManager的系统服务:

// Context objects are able to fetch or start system services.
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());


Application Context 与 Acitivity Context

尽管themes和 styles通常是在application层使用的,但他们也可以被用在activity层。activity可以设置为与application不同的样式(例如,在某些特定页面你可以选择不适用ActionBar)。你肯定注意到过AndroidManifest.xml文件中通过android:theme设置application主题。我们也可以为特定Activity设置不同的theme:

<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">

由于以上原因,我们必须了解Application Context 和Activity Conttext以及他们各自的生命周期。我们应该为大部分Views传递一个Activity Context,以便使其获得themes,styles和dimensions的相关信息。如果我们没有为当前Activity设置theme,则会默认使用application的对应设置。

多数情况下,你应该使用Activity Context。我们都知道,Java中的关键字this表示对当前类实例的引用,通常,在一个Activity内部,当你需要使用Context时,可以直接使用this。下面是一个例子:

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();
    }
}

匿名函数

当在实现listeners而需要使用匿名函数时,关键字this对应于最接近的class。在下面这个例子中,使用this时,外部类MainActivity必须被显示地申明以指向Activity实例。

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();
              }
          });
        }
    }



适配器

Array Adapter

当我们为ListView构造适配器时,通常会在布局填充的过程中使用到getContext()。这个方法使用到是实例化ArrayAdapter的context:

if (convertView == null) {
        convertView = 
            LayoutInflater
                .from(getContext())
                .inflate(R.layout.item_user, parent, false);
     }
如果你使用application context实例化ArrayAdapter,你可能会注意到themes/styles没有被使用。所以,请确保你传递的是Acitivity context。


RecyclerView Adapter

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) {
           // Very important to call the parent constructor
           // as this ensures that the imageView field is populated.
           super(imageView);

           // Perform other view lookups.
           customViewGroup = (FrameLayout) imageView.findById(R.id.customViewGroup);
       }
   }
}

刚刚提到,ArrayAdapter的构造器需要传递一个context,然而RecyclerView.Adapter并不需要。在它的布局填充过程中,可以通过其父视图来传递正确的context。

而关联的RecyclerView总是把它自己当做父视图传递到RecyclerView.Adapter.onCreateViewHolder()防范中间那个。

如果需要再onCreateViewHolder()方法外部使用context,那么只要存在一个可用的ViewHolder实例,就可以通过viewHolder.itemView.getContext()方法来获得context。这里的itemView是ViewHolder中一个public,non-null和final的字段。


避免内存泄露

当我们使用单例模式时,通常需要使用Application Context。例如,自定义一个manager类,它就需要使用Contex来获取系统服务。然后再Activity不在运行后,仍然持有一个Acitivity context的银鬃将会造成内存无法回收,这是就应该使用Application Context。

在下面的例子中,如果被传入的是一个Activity或Service context并且其已经被Android系统销毁,那么它也不能被回收利用,因为CustonManager类扔持有它的静态引用。

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;
    }
}


使用Application Context

为了避免内存泄露,千万不要在context的生命周期之外持有其引用。检查你的所有后台线程,悬挂的handlers以及其他可能持有context对象的内部类。

上面的例子应该做出修改,在CustomManager.getInstance()方法中使用application context。application是单例的并且与application的生命周期绑定,这样就可以安全的持有它的引用。修改后如下所示:

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;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值