欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  技术分享

Android工具类Context详细解析

程序员文章站 2022-03-01 12:40:20
...

一、概述

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


三、Contex创建视图

Contexts包含了以下信息、设备的屏幕大小以及将dp、sp转化为px的尺寸、style属性、onClick属性

TextView textView = new TextView(context);


四、Contex Inflate xml布局文件

我们使用context来获得LayoutInflater、其可以在内存中inflate xml布局文件

LayoutInflater inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.my_layout, parent);


五、Contex 发送本地广播

我们使用context来获得LocalBroadcastManager、其可以发送或者注册广播接收

Intent broadcastIntent = new Intent("custom-action");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);


六、Contex 获取系统服务

例如当你需要发送通知、你需要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关键字的使用

@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布局、注意:当你传入的是应用级别的context,你会发现themes/styles属性将不会被应用、所以确保你在这里传入的是Activity级别的context

if (convertView == null) {
    convertView = LayoutInflater.from(getContext())
        .inflate(R.layout.item_user, parent, false);
}


十、RecyclerView适配器

ArrayAdapter需要在其构造器里面传入context、RecyclerView.Adapter不需要、RecyclerView通常将其作为父视图传给RecyclerView.Adapter.onCreateViewHolder()

如果在onCreateViewHolder()方法的外面、你需要用到context、你也可以使用ViewHolder、例如viewHolder.itemView.getContext()、itemView是一个公有、非空、final类型的成员变量

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


十一、避免内存泄露

应用级别的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;
}