Android开发中findViewById()函数用法与简化

 更新时间:2016年9月20日 19:55  点击:3658
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。

Android中FindViewById()是一个非常常用的函数,位于android.app.Activity包中。该函数利用我们在XML文件中定义的View的id属性来获取相应的View对象。findViewById()属于API Level 1, 对应的android版本是android1.0, 由此,可以看出,该函数是android早期版本中就有的。顺便说一下, android目前市场上已商用的版本及其对应的API Level如下:

android 1.0API Level 1

android 1.1API Level 2

android 1.5API Level 3

android 1.6API Level 4

android 2.0API Level 5

android 2.0.1 API Level 6

android 2.1API Level 7

android 2.2API Level 8

1、参数错误:findViewById的参数是一个View的ID,如果在XML文件中没定义相应的ID,则程序会

善意的告诉你:XXX cannot be resulved。此时,补充定义就可以了。

2、未指定调用布局:findViewById()的调用与具体的布局有关,默认的是main.xml中的布局,函数前

 没有布局指示。不过,当我们在main.xml描述的布局中,添加其它布局时,利用该函数获

 取所添加布局中的View,则需在调用时,添加布局名,形式如下:

 addLayout.findViewById(), 如果不这么做,程序编译时有时不会报错,但运行时会

 提示遇到异常,并强制关闭应用。


3、命名冲突:这个错误可能不是很常见,不过,要是没有遇到过,猛的来这么一下,还真让人 有点懵。

 呵呵,本人就犯过这样的错误。解释下,这里的命名冲突是指当前工程中定义的类与

 android在Framework中提供的名字相同,这样的话,当前工程文件中,会优先使用本工

 程中的定义。当然,使用findViewById()函数时,发生这种错误必须满足以下几个条件:

 一是:需要在当前工程中利用ID(xml中定义)来查找对应的View对象;

 二是:查找的View类名恰好与本工程中已有的类定义相同

 三是:同名的两个类实例化后产生的对象类型不同,如:一个是View, 一个是Activity。

问题现象:

这样的代码熟悉吗?一个控件比较多的页面一直重复写这样的代码有没有很麻烦?

解决方法:


自定义一个方法:

public <T> T $(int viewID) {
 return (T) findViewById(viewID);
}

然后不管是什么类型的View,直接一个$方法搞定:


Android 反射简化findViewById .

官方例子里的小玩意。。。。。

一个注解:InjectView

 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Use this annotation to mark the fields of your Activity as being injectable.
 * <p>
 * See the {@link Injector} class for more details of how this operates.
 */
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    /**
     * The resource id of the View to find and inject.
     */
    public int value();
}

一个通过反射注解 并 实例化的功能类:


import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

import android.app.Activity;

/**
 * Very lightweight form of injection, inspired by RoboGuice, for injecting common ui elements.
 * <p>
 * Usage is very simple. In your Activity, define some fields as follows:
 *
 * <pre class="code">
 * @InjectView(R.id.fetch_button)
 * private Button mFetchButton;
 * @InjectView(R.id.submit_button)
 * private Button mSubmitButton;
 * @InjectView(R.id.main_view)
 * private TextView mTextView;
 * </pre>
 * <p>
 * Then, inside your Activity's onCreate() method, perform the injection like this:
 *
 * <pre class="code">
 * setContentView(R.layout.main_layout);
 * Injector.get(this).inject();
 * </pre>
 * <p>
 * See the {@link #inject()} method for full details of how it works. Note that the fields are
 * fetched and assigned at the time you call {@link #inject()}, consequently you should not do this
 * until after you've called the setContentView() method.
 */
public final class Injector {
    private final Activity mActivity;

    private Injector(Activity activity) {
        mActivity = activity;
    }

    /**
     * Gets an {@link Injector} capable of injecting fields for the given Activity.
     */
    public static Injector get(Activity activity) {
        return new Injector(activity);
    }

    /**
     * Injects all fields that are marked with the {@link InjectView} annotation.
     * <p>
     * For each field marked with the InjectView annotation, a call to
     * {@link Activity#findViewById(int)} will be made, passing in the resource id stored in the
     * value() method of the InjectView annotation as the int parameter, and the result of this call
     * will be assigned to the field.
     *
     * @throws IllegalStateException if injection fails, common causes being that you have used an
     *             invalid id value, or you haven't called setContentView() on your Activity.
     */
    public void inject() {
        for (Field field : mActivity.getClass().getDeclaredFields()) {
            for (Annotation annotation : field.getAnnotations()) {
                if (annotation.annotationType().equals(InjectView.class)) {
                    try {
                        Class<?> fieldType = field.getType();
                        int idValue = InjectView.class.cast(annotation).value();
                        field.setAccessible(true);
                        Object injectedValue = fieldType.cast(mActivity.findViewById(idValue));
                        if (injectedValue == null) {
                            throw new IllegalStateException("findViewById(" + idValue
                                    + ") gave null for " +
                                    field + ", can't inject");
                        }
                        field.set(mActivity, injectedValue);
                        field.setAccessible(false);
                    } catch (IllegalAccessException e) {
                        throw new IllegalStateException(e);
                    }
                }
            }
        }
    }
}

使用时类似:

 01.@InjectView(R.id.gygallery) 
02.private Gallery gallery; 
03.@InjectView(R.id.is_switcher) 
04.private ImageSwitcher imageSwitcher; 
 @InjectView(R.id.gygallery)
 private Gallery gallery;
 @InjectView(R.id.is_switcher)
 private ImageSwitcher imageSwitcher;

Activity>onCreate(){


Injector.get(this).inject();//init views

}

下面我们来为各位整理一些关于ListView中的Item点击事件和子控件的冲突或者item点击没有反应问题解决办法了,具体的操作如下所示。

fragment中添加了button和checkbox这些控件,此时这些子控件会将焦点获取到,所以常常当点击item时变化的是子控件,item本身的点击没有响应。

这时候就可以使用descendantFocusability来解决啦,API描述如下:
android:descendantFocusability

该属性是当一个为view获取焦点时,定义viewGroup和其子控件两者之间的关系。

属性的值有三种:

beforeDescendants:viewgroup会优先其子类控件而获取到焦点

afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点

blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点

通常我们用到的是第三种,即在Item布局的根布局加上android:descendantFocusability=”blocksDescendants”的属性。

写这个demo顺便复习一下BaseAdapter

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class Audition1 extends Activity {

    private ListView listView;
a
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audition1);
        initComponents();

    }

    private void initComponents() {
        listView = (ListView) findViewById(R.id.listView);
        listView.setAdapter(new MyAdapter(this));
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) {
                Toast.makeText(getApplicationContext(), "item", 300).show();

            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_audition1, menu);
        return true;
    }

    public final class ViewHolder {
        public TextView textView;
        public Button button;
        public ImageView imageView;
    }

    class MyAdapter extends BaseAdapter {

        private LayoutInflater mInflater;

        public MyAdapter(Context context) {
            this.mInflater = LayoutInflater.from(context);
        }

        @Override
        public int getCount() {
            return 3;
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                holder = new ViewHolder();

                convertView = mInflater.inflate(R.layout.item, null);
                holder.imageView = (ImageView) convertView
                        .findViewById(R.id.imageView);
                holder.textView = (TextView) convertView
                        .findViewById(R.id.textViewId);
                holder.button = (Button) convertView.findViewById(R.id.button);
                holder.textView.setText("shit");
                holder.button.setOnClickListener(new View.OnClickListener() {

                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(
                                Audition1.this);
                        builder.setMessage("dialog");
                        builder.setTitle("title");
                        builder.create();
                        builder.show();
                    }
                });
                convertView.setTag(holder);
            } else {
                convertView.getTag();
            }

            return convertView;
        }

    }

}


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ListView
        android:id="@+id/listView"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:padding="@dimen/padding_medium"
        tools:context=".Audition1"
        android:dividerHeight="5dp"/>

</RelativeLayout>?

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
 
    <TextView 
        android:id="@+id/textViewId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        />
    <ImageView 
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        android:contentDescription="@string/app_name"
        />
 
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="press"
        android:focusable="false"
        />
 
</LinearLayout>

原因是button强制获取了item的焦点,只要设置button的focusable为false即可。

本文我在网上总结了几篇关于禁止Android横竖屏,解决切换屏幕时重启Activity的方法,解决Android手机 屏幕横竖屏切换,如何让Android横竖屏切换时不销毁当前activity。

禁止Android横竖屏和解决切换屏幕时重启Activity的方法

1.在AndroidManifest.xml的Activity配置中加入 android:screenOrientation=”landscape”属性(landscape是横向,portrait是纵向)。如:

<activity android:name=".ContactsManagerActivity" android:label="@string/app_name" android:screenorientation="portrait">
    <intent-filter>
        <action android:name="android.intent.action.MAIN">
        <category android:name="android.intent.category.LAUNCHER">
    </category></action></intent-filter>
</activity>

        
2.一般横竖屏切换时,activity要重启,为了避免重启,可以为activity 添加android:configChanges=“orientation”属性,然后在activity中复写onConfigurationChanged()方法,例如:

public void onConfigurationChanged(Configuration newConfig) {
    // TODO Auto-generated method stub
    if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE) {
        setContentView(R.layout.imageswitch);
        //横屏
    } else {
        setContentView(R.layout.editcontact);//竖屏
    }
    
    super.onConfigurationChanged(newConfig);
}


这样就可以实现不重启activity,实现横竖屏切换了。



解决Android手机 屏幕横竖屏切换

Android中当屏幕横竖屏切换时,Activity的生命周期是重新加载(说明当前的Activity给销毁了,但又重新执行加载),怎么使屏幕横竖屏切换时,当前的Activity不销毁呢?

1. 在AndroidManifest.xml中为Activity设置configChanges属性,

application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>


configChanges有如下选项:  1. orientation :屏幕在纵向和横向间旋转,  2. keyboardHidden:键盘显示或隐藏 ,3.fontScale:用户变更了首选的字体大小   4.locale : 用户选择了不同的语言设定,5.  keyboard :键盘类型变更,例如手机从12键盘切换到全键盘   6. touchscreen或navigation:键盘或导航方式变化,

如果缺少了keyboardHidden选项 不能防止Activity的销毁,并且在之后提到的onConfigurationChanged事件中 只能捕获竖屏变横屏的事件 不能捕获横屏变竖屏


2. 在对应的Activity中重写:onConfigurationChanged 方法:

public class MainActivity extends Activity {
    private TextView textView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.i("--Main--", "onCreate");
        textView=(TextView)findViewById(R.id.tv_id);
    }
    
    
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.i("--Main--", "onConfigurationChanged");
        if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
            textView.setText("当前屏幕为横屏");
        }else{
            textView.setText("当前屏幕为竖屏");
        }
    }
    
}



布局文件就是一个简单的TextView此处不给出,

效果如下:

日志打印:


从日志中可以分析出屏幕横竖屏切换时Activity并没有销毁,当然你也可以运行项目在onCreate方法打个断点,执行发现onCreate方法只是在刚开始进入时执行,屏幕横竖屏切换时,已经不会在执行,因此可在onConfigurationChanged方法中下点文章!

注:如果项目不需要屏幕切换时可以设置为

1. android:screenOrientation="portrait" 始终以竖屏显示
2. android:screenOrientation="landscape" 始终以横屏显示

上面的配置文件设置屏幕横竖屏,下面是代码去控制屏幕横竖屏的:

private OnClickListener onClick=new OnClickListener() {
    @Override
    public void onClick(View v) {
        //设置屏幕为横屏
        if(v==butLandscrpe){
            MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        //设置为置屏幕为竖屏
        }else{
            MainActivity.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
};
//监听系统设置的更改
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    String message=newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE ? "屏幕设置为:横屏" : "屏幕设置为:竖屏";
    showToast(message);
}




横竖屏切换时候Activity的生命周期

1、新建一个Activity,并把各个生命周期打印出来
2、运行Activity,得到如下信息
onCreate-->onStart-->onResume-->
3、按crtl+f12切换成横屏时
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
4、再按crtl+f12切换成竖屏时,发现打印了两次相同的log
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
5、修改AndroidManifest.xml,把该Activity添加 android:configChanges="orientation",执行步骤3
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->
6、再执行步骤4,发现不会再打印相同信息,但多打印了一行onConfigChanged
onSaveInstanceState-->onPause-->onStop-->onDestroy-->onCreate-->onStart-->onRestoreInstanceState-->onResume-->onConfigurationChanged-->
7、把步骤5的android:configChanges="orientation" 改成 android:configChanges="orientation|keyboardHidden",执行步骤3,就只打印onConfigChanged
onConfigurationChanged-->
8、执行步骤4
onConfigurationChanged-->onConfigurationChanged-->
 总结:
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
 

总结一下整个Activity的生命周期
补充一点,当前Activity产生事件弹出Toast和AlertDialog的时候Activity的生命周期不会有改变
Activity运行时按下HOME键(跟被完全覆盖是一样的):onSaveInstanceState --> onPause --> onStop      onRestart -->onStart--->onResume
Activity未被完全覆盖只是失去焦点:onPause--->onResume


如何让Android横竖屏切换时不销毁当前activity

我们都知道android 屏幕横向或竖向相互转换的时候会销毁当前的Activity 然后重启它。
为了横竖屏切换时不销毁当前Activity 我来介绍两种方法。

方法一:不允许横竖屏切换:

实现方法: Activity中还有一属性和屏幕方向有关:

android:screenOrientation=["unspecified" | "user" | "behind" | "landscape" | "portrait" | "sensor" | "nosensor"]
在Mainifest.xml的Activity元素中增加这么一个属性:
android:screenOrientation=”portrait”则无论手机如何变动,拥有这个属性的activity都将是竖屏显示。
android:screenOrientation=”landscape”,为横屏显示。


//--------------------------------------------------------------------------------------------

方法二:

首先在Mainifest.xml的Activity元素中加入android:configChanges=”orientation|keyboardHidden”属性:

加入这条属性的含义是,应用程序将会处理屏幕方向和键盘状态(推出或合上)信息的改动。

但对于其他的设备配置信息的改动则会由Android系统来处理(销毁当前Activity,然后重启一个新的Activity实例)

然后 在java代码的activity子类中加入配置信息改动的处理代码:

 

//-----------------------------------------------------------------------------------------
@Override public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
// 检测屏幕的方向:纵向或横向
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
//当前为横屏, 在此处添加额外的处理代码
} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
//当前为竖屏, 在此处添加额外的处理代码
}
//检测实体键盘的状态:推出或者合上
if (newConfig.hardKeyboardHidden ) == Configuration.HARDKEYBOARDHIDDEN_NO)
{
//实体键盘处于推出状态,在此处添加额外的处理代码
} else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES)
{
//实体键盘处于合上状态,在此处添加额外的处理代码
}
}


现在大部分人开发Android应用是在Android Studio上进行,不过以前网上很多的开源库,控件等还是以前的基于Eclipse进行开发,很多人不知道怎么导入到自己的基于Android Studio项目中来本文详细介绍一下。

本文我们将详细介绍基于Eclipse的开源库如何导入Android Studio项目中。

前言

    --project //项目目录
    |
    build.gradle //项目的gradle配置文件
    |
    settings.gradle //gradle设置,会保存所有的module
    |
    app //module 目录
    |__build.gradle module的配置
    |
    module2 //module2目录
    |__build.gradle module的配置

同eclipse中的项目一样,gradle/android studio 构建也可以有module,将moudle放到项目目录下面,然后在settings.gradle中增加该module,最简单的方法是用文件夹名。比 如我们上面的结构,build.gradle文件应该如下:

    include ':app', ':module2'

更多关于gralde的知识可以看我以前的文章:

使用gradle构建android项目(续)

使用Gradle构建Android项目

导入Jar文件

这种可能很常见,可以下载到别人搞好的jar包,这样可以直接在自己的主module下创建libs文件夹(我这里这样,只是为了兼容eclipse方式),然后把jar文件放进去,然后在module的build.gradle文件中的dependecies{}添加如下代码:

    compile files('libs/name.jar')

当libs文件夹下面有多个文件时,可以用一句代码包含这些包:

    compile fileTree(dir: 'libs', include: ['*.jar'])

当有文件不需要被包含时,可以这样:

    compile fileTree(dir: 'libs', exclude: ['android-support*.jar'], include: ['*.jar'])

从上面的代码中可以看到我们可以使用通配符, +表示一个字符,*表示0到多个字符。

导入maven中的库

如果开源库作者有将代码放到Maven库中,我们可以在gradle配置中直接引入,类似如下:

    compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.1'

一般我们可以在开源库的github页面上面看有没有这样一个地址,或者到maven库中根据包名搜索有没有,我们前面这个引入的项目分三个部分 group:name:version,我们引入其他的包也有遵守这个规则。

导入gradle构建的开源库

这种情况的比较少用到,因为这张的开源库,作者一般都有放到maven库中,但是偶尔也会用到这里也提一下。

首先下载文件,将我们需要的这个库的module文件夹拷贝到我们的项目的目录下面,然后在setting.gradle文件中增加文件夹名称, 然后在我们需要依赖这个模块的module中的build.gradle文件中加入如下代码:

    compile project(':libmodule')

这样就可以了。

导入基于Eclipse构建的开源库

基于Eclipse构建的项目,和基于Android Studio构建的项目的很大区别是目录结构不同。

我们首先将module文件夹拷 贝到我们的项目目录下面,然后在settings.gradle文件中增加这个module,然后在要使用的module中的build.gradle文 件中引入依赖,这样看的话,似乎和引入基于gradle构建的没什么不同。但是,基于Eclipse构建的项目中,没有build.gradle文件,所 以我们需要自己新建一个放到module下面,下面是一个模版:

    apply plugin: 'android-library'
    repositories {
    mavenCentral()
    }
    android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"
    defaultConfig {
    minSdkVersion 9
    targetSdkVersion 19
    }
    sourceSets {
    main {
    manifest.srcFile 'AndroidManifest.xml'
    java.srcDirs = ['src']
    resources.srcDirs = ['src']
    aidl.srcDirs = ['src']
    res.srcDirs = ['res']
    assets.srcDirs = ['assets']
    jniLibs.srcDirs = ['libs']
    }
    }
    lintOptions {
    abortOnError false
    }
    }
    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    }

当然,根据各自的sdk和buildtools版本等等,以及其他,配置会有变化,可以看我之前的文章。

其他

以上就是主要的集中导入场景,自己可以根据自己的实际情况然后改变配置等等。

另外,我们导入的仓库可能不是maven中心仓库,或者可能是我们自己搭建的仓库,我们可以自定义仓库地址的,修改build.gradle文件中的repositories就可以了,例如:

    buildscript {
    repositories {
    jcenter()
    mavenCentral()
    maven {
    url "https://oss.sonatype.org/content/repositories/snapshots"
    }
    }
    }

另外,project层的buildscript在module层也是会生效的,所以不用在每个module都配置。




Android Studio如何导入jar,so,以及第三方的开源库?

问:在Github上的许多活跃项目已经开始采用Android Studio编写了,看来这场趋势已经无法阻挡。所以我也开始从Eclipse阵营转入Studio。刚开始玩Studio各种不适应,希望各位大大能给个详细的教程。。。

回答一:

一般用eclipse生成的项目,导入android studio都会有各种问题,不过要解决也不难,简单几步设置就可以了,说几个比较常用的步骤:

1.导入主项目之后,需要在File -> project strucure的modules选项设置好各种路径,比如你的src、libs、gen、bin、AndroidManifest.xml的路径等等

2.在eclipse里的project,在as上就变成module了,所以要用import module的方式引入库项目,之后可能也要重复步骤1给库项目设置好路径

3.jar文件还是放在libs文件夹,然后需要在project strucure里的libraries选项引入进来

4.库项目和各种jar都导进来了,但是主项目还是报错,这可能是因为主项目和这些库文件的依赖关系没关联好,还是project strucure的主项目里面,选择dependencis,把需要用到的module和library都添加进来

所以关键还是在对project strucure的设置 :)

另一种用Guide构建的项目,好处什么的就不细说了,网上一搜一大把,教程可以看android官网的使用指南,有兴趣可以了解下


回答二:

一般 Android 项目从 Eclipse 导入至 Android Studio(以下简称AS) 后,会出现各种问题,最主要的就是 “gradle” 目前为止还不支持 .so 库文件打包入 apk 。
也就是说,如果你用的第三方库中包含 .so 文件 (一般是 libs\armeabi\ xxx.so ) , 那么只要在代码中出现 “System.loadLibrary( "xxx" ) ; 等代码出现,一定会在这里抛出异常 UnsatisfiedLinkError 。

解决方案如下: 根据我 Google 了2天的结果,一个比较满意且所有出现这个问题的人都可行的方案是:
1) 在硬盘任意位置建立空文件夹 "lib" (注意名字是 "lib" 而不是 "libs" )
2) 把原来的 libs 目录下的 armeabi 文件夹拷贝至 刚建立的 lib 目录下. (armeabi文件夹中的.so文件也会被一并拷入)
3) 将刚建立的 "lib" 目录打包压缩成 .zip 文件, 同时改名为 “armeabi.jar"
4) 将 armeabi.jar 放入原来的 libs 目录下 (和其他第三方jar包放在一起)

经过这4步之后,只需要再重新更新下AS的第三方库,就OK了 .

Volley是Google I/O大会上推出了一个新的网络通信框架,它把AsyncHttpClient和Universal-Image-Loader的优点集于了一身。本文我们重点讲讲Volley的基本用法及使用实例。

1. 什么是Volley

我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据。Android 系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高。

不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码。于是乎,一些Android网络通信框架也就应运而生,比如说AsyncHttpClient,它把HTTP所有的通信细节全部封装在了内部,我们只需要简单调用几行代码就可以完成通信操作了。再比如Universal-Image-Loader,它使得在界面上显示网络图片的操作变得极度简单,开发者不用关心如何从网络上获取图片,也不用关心开启线程、回收图片资源等细节,Universal-Image-Loader已经把一切都做好了。

Android开发团队也是意识到了有必要将HTTP的通信操作再进行简单化,于是在2013年Google I/O大会上推出了一个新的网络通信框架——Volley。Volley可是说是把AsyncHttpClient和Universal-Image- Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image- Loader一样轻松加载网络上的图片。除了简单易用之外,Volley在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。

下图所示的这些应用都是属于数据量不大,但网络通信频繁的,因此非常适合使用Volley。


2. 下载Volley

介绍了这么多理论的东西,下面我们就准备开始进行实战了,首先需要将Volley的jar包准备好,如果你的电脑上装有Git,可以使用如下命令下载Volley的源码:

git clone https://android.googlesource.com/platform/frameworks/volley

下载完成后将它导入到你的Eclipse工程里,然后再导出一个jar包就可以了。如果你的电脑上没有Git,那么也可以直接使用我导出好的jar包,下载地址是:http://www.kwstu.com/ResourcesView/kwstu_201441183330928 。

新建一个Android项目,将volley.jar文件复制到libs目录下,这样准备工作就算是做好了。

3. StringRequest的用法

前面已经说过,Volley的用法非常简单,那么我们就从最基本的HTTP通信开始学习吧,即发起一条HTTP请求,然后接收HTTP响应。首先需要获取到一个RequestQueue对象,可以调用如下方法获取到:

RequestQueue mQueue = Volley.newRequestQueue(context);

注意这里拿到的RequestQueue是一个请求队列对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。 RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上在每一个需要和网络交互的Activity中创建一个RequestQueue对象就足够了。

接下来为了要发出一条HTTP请求,我们还需要创建一个StringRequest对象,如下所示:

StringRequest stringRequest = new StringRequest("http://www.baidu.com",
                        new Response.Listener<String>() {
                            @Override
                            public void onResponse(String response) {
                                Log.d("TAG", response);
                            }
                        }, new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                Log.e("TAG", error.getMessage(), error);
                            }
                        });


可以看到,这里new出了一个StringRequest对象,StringRequest的构造函数需要传入三个参数,第一个参数就是目标服务器的URL 地址,第二个参数是服务器响应成功的回调,第三个参数是服务器响应失败的回调。其中,目标服务器地址我们填写的是百度的首页,然后在响应成功的回调里打印出服务器返回的内容,在响应失败的回调里打印出失败的详细信息。

最后,将这个StringRequest对象添加到RequestQueue里面就可以了,如下所示:

mQueue.add(stringRequest);

另外,由于Volley是要访问网络的,因此不要忘记在你的AndroidManifest.xml中添加如下权限:

好了,就是这么简单,如果你现在运行一下程序,并发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据。


没错,百度返回给我们的就是这样一长串的HTML代码,虽然我们看起来会有些吃力,但是浏览器却可以轻松地对这段HTML代码进行解析,然后将百度的首页展现出来。

这样的话,一个最基本的HTTP发送与响应的功能就完成了。你会发现根本还没写几行代码就轻易实现了这个功能,主要就是进行了以下三步操作:

1. 创建一个RequestQueue对象。

2. 创建一个StringRequest对象。

3. 将StringRequest对象添加到RequestQueue里面。

不过大家都知道,HTTP的请求类型通常有两种,GET和POST,刚才我们使用的明显是一个GET请求,那么如果想要发出一条POST请求应该怎么做呢?StringRequest中还提供了另外一种四个参数的构造函数,其中第一个参数就是指定请求类型的,我们可以使用如下方式进行指定:

StringRequest stringRequest = new StringRequest(Method.POST, url, listener, errorListener);

可是这只是指定了HTTP请求方式是POST,那么我们要提交给服务器的参数又该怎么设置呢?很遗憾,StringRequest中并没有提供设置POST 参数的方法,但是当发出POST请求的时候,Volley会尝试调用StringRequest的父类——Request中的getParams()方法来获取POST参数,那么解决方法自然也就有了,我们只需要在StringRequest的匿名类中重写getParams()方法,在这里设置POST 参数就可以了,代码如下所示:

StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        Map<String, String> map = new HashMap<String, String>();
        map.put("params1", "value1");
        map.put("params2", "value2");
        return map;
    }
};

你可能会说,每次都这样用起来岂不是很累?连个设置POST参数的方法都没有。但是不要忘记,Volley是开源的,只要你愿意,你可以自由地在里面添加和修改任何的方法,轻松就能定制出一个属于你自己的Volley版本。

4. JsonRequest的用法

学完了最基本的StringRequest的用法,我们再来进阶学习一下JsonRequest的用法。类似于 StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和 JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。

至于它们的用法也基本上没有什么特殊之处,先new出一个JsonObjectRequest对象,如下所示:

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html", null,
        new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                Log.d("TAG", response.toString());
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e("TAG", error.getMessage(), error);
            }
        });

可以看到,这里我们填写的URL地址是http://m.weather.com.cn/data/101010100.html,这是中国天气网提供的一个查询天气信息的接口,响应的数据就是以JSON格式返回的,然后我们在onResponse()方法中将返回的数据打印出来。

最后再将这个JsonObjectRequest对象添加到RequestQueue里就可以了,如下所示:

mQueue.add(jsonObjectRequest);

这样当HTTP通信完成之后,服务器响应的天气信息就会回调到onResponse()方法中,并打印出来。现在运行一下程序,发出这样一条HTTP请求,就会看到LogCat中会打印出如下图所示的数据。


由此可以看出,服务器返回给我们的数据确实是JSON格式的,并且onResponse()方法中携带的参数也正是一个JSONObject对象,之后只需要从JSONObject对象取出我们想要得到的那部分数据就可以了。

你应该发现了吧,JsonObjectRequest的用法和StringRequest的用法基本上是完全一样的,Volley的易用之处也在这里体现出来了,会了一种就可以让你举一反三,因此关于JsonArrayRequest的用法相信已经不需要我再去讲解了吧。


Android Volley网络框架使用


在Android中,网络请求无非就这两种:HttpURLConnection和HttpClient( Apache),我们在使用时一般都会对它们进行一系列的封装,但是这过程不免有些繁琐,所以,Google官方也考虑到了这点,在2013年Google I/O大会上就推出了一个新的网络请求框架——Volley,它将各种网络请求都简单化,并且把AsyncHttpClient和Universal-Image-Loader两大框架的优点集一身,Volley用在数据量不大的网络请求操作时它的性能表现的非常出色,但是Volley如果在进行数据量大的网络操作时(下载文件等),那么Volley将表现的比较糟糕。

Volley有这么几大功能:

    1、普通数据、JSON、图片的异步加载
    2、网络请求优先级处理
    3、自带硬盘缓存(普通数据、图片、JSON),另外我们在加载图片时候通过ImageLoader还可加入LruCache
    4、取消请求
    5、与Activity生命周期联动(Activity退出时同时取消所有的请求)

可见,Volley框架是非常强大的,下面我就一一介绍怎么使用Volley框架。

    Volley框架的原理:它内部是通过一个请求队列(RequestQueue)来维护所有请求,我们新创建一个请求(request)后通过RequestQueue.add()方法将请求添加置请求队列中,然后调用RequestQueue.start()方法执行请求队列中的方法

Volley中包含这么几种类型的请求:

    StringRequest - 返回字符串数据
    JsonObjectRequest - 返回JSONArray数据
    JsonArrayRequest - 返回JSONObject数据
    ImageRequest - 返回Bitmap类型数据

当然使用前我们必须导入Volley.jar包(可以去网上下载),或者通过git下载

git clone https://android.googlesource.com/platform/frameworks/volley

这里给出我上传的jar包下载地址:Volley.jar
创建RequestQueue请求队列

RequestQueue是通过Volley的静态方法newRequestQueue来创建的:

RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());

一般我们会继承自Application在自定义的MyApplication中创建一个全局的请求队列,用来维护app中的网络请求。


StringRequest

这里主要讲最常用的GET和POST请求方式:
这里我用聚合网上查询手机号码归属地的数据为例子,我们创建一个StringRequest请求,然后给该请求设置一个Tag,用来标记这个请求,取消请求时候我们可以通过这个Tag来取消某个或者所有请求,再把该请求加入请求队列,最后执行请求队列中的请求。

StringRequest的构造方法为:

/**
* @method 请求方式(GET、POST等)
* @url 请求url
* @listener 请求成功回调的接口
* @errorListener 请求失败回调的接口
*/
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)

GET

一个完整的StringRequest的GET请求如下:

String url = "http://apis.juhe.cn/mobile/get?phone=18270837821&key=9a4329bdf84fa69d193ce601c22b949d";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());//创建一个请求队列
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
    @Override
    public void onResponse(String s) {
        Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {
        Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
    }
});
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();

POST

一个完整的StringRequest的POST请求如下:

String url = "http://apis.juhe.cn/mobile/get";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
StringRequest request = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
    @Override
    public void onResponse(String s) {
        Toast.makeText(getApplicationContext(),s,Toast.LENGTH_SHORT).show();
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {
        Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
    }
}){
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        Map<String,String> map  =new HashMap<>();
        map.put("phone","18270837821");
        map.put("key","9a4329bdf84fa69d193ce601c22b949d");
        return map;
    }
};
request.setTag("zxy");
mRequestQueue.add(request);
mRequestQueue.start();

其中,因为是以POST方式请求数据的,所以我们必须实现StringRequest的getParams()方法,该方法返回的是Map<String, String>类型的集合,也就是用<key,value>的形式把数据通过POST传入服务器


JsonObjectRequest

JsonObjectRequest构造方法为:

/**
* @method 请求方式(GET、POST等)
* @url 请求url
* @jsonRequest 请求传入的json数据
* @listener 请求成功回调的接口
* @errorListener 请求失败回调的接口
*/
public JsonObjectRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)

GET

一个完整的JsonObjectRequest的GET请求如下:
因为用的是GET请求方式,参数是在url中传入,所以JSONObject对象传入null

String url = "http://apis.juhe.cn/mobile/get?phone=18270837821&key=9a4329bdf84fa69d193ce601c22b949d";
    RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject jsonObject) {
            Toast.makeText(getApplicationContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
        }
    });

    request.setTag("zxy");
    mRequestQueue.add(request);
    mRequestQueue.start();

POST

一个完整的JsonObjectRequest的POST请求如下:

String url = "http://apis.juhe.cn/mobile/get";
    RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    JSONObject jsonObject = new JSONObject();
    try {
        jsonObject.put("phone", "18270837821");
        jsonObject.put("key", "9a4329bdf84fa69d193ce601c22b949d");
    } catch (JSONException e) {
        e.printStackTrace();
    }
    JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, url, jsonObject, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject jsonObject) {
            Toast.makeText(getApplicationContext(),jsonObject.toString(),Toast.LENGTH_SHORT).show();
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
        }
    });
    request.setTag("zxy");
    mRequestQueue.add(request);
    mRequestQueue.start();

用以上StringRequest和JSONObjectRequest请求我们都获取到了数据,如图
01.png


虽然这两种方式都可以返回我们请求的数据,但是JSONObjectRequest请求在处理json对象的返回结果时候效率更高,所以确定返回结果是json类型时候可以使用JSONObjectRequest


ImageRequest

ImageRequest的构造方法为:

/**
* @url 请求url
* @listener 请求成功回调的接口
* @maxWidth 图片最大的宽度(如果超过则Volley会对图片进行压缩,如果为0则不压缩)
* @maxHeight 图片最大的高度
* @decodeConfig 图片的配置
* @errorListener 请求失败回调的接口
*/
public ImageRequest(String url, Listener<Bitmap> listener, int maxWidth, int maxHeight, Config decodeConfig, ErrorListener errorListener)

普通的加载方式(ImageView显示)

一个标准的普通请求网络图片的方法:

String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
    RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    ImageRequest request = new ImageRequest(url, new Response.Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap bitmap) {
            mImageView.setImageBitmap(bitmap);
        }
    }, 0, 0, Bitmap.Config.ARGB_8888, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {
            Toast.makeText(getApplicationContext(),volleyError.toString(),Toast.LENGTH_SHORT).show();
        }
    });
    request.setTag("zxy");
    mRequestQueue.add(request);
    mRequestQueue.start();
}

这种方式只适合在图片数量不多的情况下使用,否则则需要考虑使用下面两种,同样,因为Volley内部本身就有硬盘缓存机制,在没网的情况下则会加载缓存中的图片


带LruCache缓存的加载方式(ImageView+ImageLoader+ImageCache)

我们上一种方法就是使用Volley中的ImageRequest请求的加载网络图片,这种方法没有LruCache和显示效果不好,下面我们来使用Volley中的ImageLoader+ImageCache的加载方式,这种方式可以在网络差的情况下和加载出错的情况下给出一个默认的提示图片,而且使用了LruCache缓存可以避免OOM。
首先我们创建一个ImageLoader对象,ImageLoader构造方法

public ImageLoader(RequestQueue queue, ImageLoader.ImageCache imageCache)

需要传入请求队列对象和ImageCache对象,我们再来看看ImageCache对象

public interface ImageCache{...}

发现它是ImageLoader内部的一个接口,所以我们得实现这个接口然后传入,于是我们创建一个BimapCache实现ImageCache接口:

public class BitmapCache implements ImageLoader.ImageCache {
    private LruCache<String, Bitmap> mBitmapLruCache;

    public BitmapCache() {
        int maxCache = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxCache / 8;
        mBitmapLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
    }

    @Override
    public Bitmap getBitmap(String key) {
        return mBitmapLruCache.get(key);
    }

    @Override
    public void putBitmap(String key, Bitmap bitmap) {
        mBitmapLruCache.put(key, bitmap);
    }
}

其实这就是创建一个图片内存缓存对象。

之后我们再使用ImageLoader.get()方法加载网络图片,我们来看看get()方法的参数:

/**
* @requestUrl - 请求url
* @listener - ImageLoader.ImageListener的监听对象
* @maxWidth - 图片的最大高度,如果超过则会压缩,为0则不压缩
* @maxHeight
*/
public ImageLoader.ImageContainer get(String requestUrl, ImageLoader.ImageListener listener, int maxWidth, int maxHeight)

第一个参数好办,那么第二个参数我们可以通过ImageLoader.getImageListener()来得到,我们来看看它的参数:

/**
* @view - 表示将图片设置到哪个控件对象上
* @defaultImageResId- 默认时显示的图片的资源id
* @errorImageResId- 加载出错时显示的图片的资源id
*/
public static ImageLoader.ImageListener getImageListener(final ImageView view, final int defaultImageResId, final int errorImageResId)

好了一切都好办了,一个带LruCache缓存的加载图片的标准请求:

String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
        RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        ImageLoader mImageLoader = new ImageLoader(mRequestQueue,new BitmapCache());
        ImageLoader.ImageListener imageListener = ImageLoader.getImageListener(mImageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
        mImageLoader.get(url,imageListener);

效果图:

01.gif
【注意】:mImageLoader.get(url,imageListener)加载图片的原理是什么呢?其实从源码中很容易看出,它首先会得到url对应的key,然后判断硬盘缓存中是否含有该key的图片,如果有则取出,没有则通过网络请求重新加载图片,所以使用这种双缓存的方式加载网络图片,可以有效的防止OOM

使用NetworkImageView+ImageLoader+ImageCache

Volley框架中对图片的请求做的特别好,其中还为我们提供了一个专门用于显示图片的控件:NetworkImageView,该控件继承自ImageView,除了拥有ImageView控件的功能之外,还多了三个方法:

public void setImageUrl(String url, ImageLoader imageLoader) {
        this.mUrl = url;
        this.mImageLoader = imageLoader;
        this.loadImageIfNecessary(false);
    }

public void setDefaultImageResId(int defaultImage) {
        this.mDefaultImageId = defaultImage;
    }

public void setErrorImageResId(int errorImage) {
        this.mErrorImageId = errorImage;
    }

    setDefaultImageResId - 设置该控件默认时显示的图片
    setErrorImageResId - 设置加载网络图片失败时显示的图片
    setImageUrl - 从网络上加载图片

使用NetworkImageView需要在layout中替换掉ImageView:

<com.android.volley.toolbox.NetworkImageView
        android:id="@+id/net_img"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


同样这里也需要使用ImageLoader和ImageCache,一个标准使用NetworkImageView的例子为:

String url="http://img.my.csdn.net/uploads/201507/21/1437459520_6685.jpg";
RequestQueue mRequestQueue = Volley.newRequestQueue(getApplicationContext());
ImageLoader mImageLoader = new ImageLoader(mRequestQueue,new BitmapCache());
mNetworkImageView.setDefaultImageResId(R.mipmap.ic_launcher);//设置默认显示的图片
mNetworkImageView.setErrorImageResId(R.mipmap.ic_launcher);//设置加载出错时显示的图片
mNetworkImageView.setImageUrl(url,mImageLoader);

效果为:
01.gif

可以看到用NetworkImageView和第二种方式效果是一样的,它同样会先检查硬盘缓存中有没有该图片,如果没有,再通过网络加载得到该图片,如果有则直接设置。既然和第二种一样,那为什么还推出NetworkImageView这个控件呢?答案就是NetworkImageView这个控件在你的Activity退出时候会自动取消网络请求,即完全不需要我们担心网络请求生命周期的问题。

【注意】:在上述两种使用了LruCache缓存加载图片的方法,当图片量较大时推荐使用,否则当你只有几张图片则使用第一种比较好,因为你使用LruCache时需要为它分配一定的内存空间,而图片量不大时候也使用LruCache缓存,那这块空间则一直是作为缓存图片用的,占着这块内存空间,相反反而会得不偿失。

Volley与Activity生命周期联动与取消请求

其实就是在Activity退出时候或销毁时候,取消对应的网络请求,避免网络请求在后台浪费资源,所以,我们一般在onStop()方法中通过之前设置的Tag取消网络请求:

@Override
protected void onStop() {
    super.onStop();
    mRequestQueue.cancelAll("zxy");
}


RequestQueue.cancelAll()方法会过滤出Tag为“zxy”的所有请求都一并取消。
或者通过Request.cancel()取消请求,把当前Activity的请求放入一个List集合中,关闭Activity时分别取消。

[!--infotagslink--]

相关文章

  • php正确禁用eval函数与误区介绍

    eval函数在php中是一个函数并不是系统组件函数,我们在php.ini中的disable_functions是无法禁止它的,因这他不是一个php_function哦。 eval()针对php安全来说具有很...2016-11-25
  • php中eval()函数操作数组的方法

    在php中eval是一个函数并且不能直接禁用了,但eval函数又相当的危险了经常会出现一些问题了,今天我们就一起来看看eval函数对数组的操作 例子, <?php $data="array...2016-11-25
  • Python astype(np.float)函数使用方法解析

    这篇文章主要介绍了Python astype(np.float)函数使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-08
  • Python中的imread()函数用法说明

    这篇文章主要介绍了Python中的imread()函数用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-16
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中using的三种用法

    using 指令有两个用途: 允许在命名空间中使用类型,以便您不必限定在该命名空间中使用的类型。 为命名空间创建别名。 using 关键字还用来创建 using 语句 定义一个范围,将在此...2020-06-25
  • 金额阿拉伯数字转换为中文的自定义函数

    CREATE FUNCTION ChangeBigSmall (@ChangeMoney money) RETURNS VarChar(100) AS BEGIN Declare @String1 char(20) Declare @String2 char...2016-11-25
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • PHP用strstr()函数阻止垃圾评论(通过判断a标记)

    strstr() 函数搜索一个字符串在另一个字符串中的第一次出现。该函数返回字符串的其余部分(从匹配点)。如果未找到所搜索的字符串,则返回 false。语法:strstr(string,search)参数string,必需。规定被搜索的字符串。 参数sea...2013-10-04
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • PHP函数分享之curl方式取得数据、模拟登陆、POST数据

    废话不多说直接上代码复制代码 代码如下:/********************** curl 系列 ***********************///直接通过curl方式取得数据(包含POST、HEADER等)/* * $url: 如果非数组,则为http;如是数组,则为https * $header:...2014-06-07
  • php中的foreach函数的2种用法

    Foreach 函数(PHP4/PHP5)foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。...2013-09-28
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • C语言中free函数的使用详解

    free函数是释放之前某一次malloc函数申请的空间,而且只是释放空间,并不改变指针的值。下面我们就来详细探讨下...2020-04-25