LruCache:从网络加载图片缓存实例
OOM异常
堆内存用于存储实例对象,当程序不断创建对象,并且对象都有引用指向,那么垃圾回收机制就不会清理这些对象,当对象多到挤满堆内存的上限后,就产生OOM异常。Android系统为每个应用程序使用的内存设置了一个上限。这个上限值可以用下面的方法取得: long maxSize = Runtime.getRuntime().maxMemory();
OOM异常通常分为下面几种情况:
1.内存泄漏导致的OOM:new出来的很多对象已经不需要了,但仍然有引用指向,所以垃圾回收机制无法回收。
其场景类似于:创建了一个Handler,并且执行了一个Delay的任务没有完成,此时拥有此Handler对象的宿主对象亦不能被回收。
或者,static的方法或成员太多,被外部使用,而外部的牵引对象没有对其进行释放,那么整个static的类都不会被释放,也就造成内存泄漏。
2.内存溢出:new出来的对象都是需要的,但堆内存太小装不下了。
如在一个GridView中显示图片,如果不压缩图片,采用为1920*1080的32位图片,每个将占用1920*1080*4=7MB内存,如此大的图片,只能存储几张,超出即可导致内存溢出。
3.关于 Bitmap 引起的泄漏,网上还有另一种说法:
一个进程的内存可以由2个部分组成:java使用内存,C使用内存,这两个内存的和必须小于16M(假设为这么多),不然就会出现大家熟悉的OOM,这个就是第一种OOM的情况。
一旦内存分配给Java后,以后这块内存即使释放后,也只能给Java的使用,这个估计和java虚拟机里把内存分成好几块进行缓存的原因有关。
所以如果Java突然占用了一个大块内存,即使很快释放了,C代码也无法使用,而Bitmap的生成是通过malloc进行内存分配的,占用的是C的内存,如果Bitmap需要的内存大于C可用内存也会导致OOM。
避免OOM
对于java中不再使用的资源需要尽快的释放,即设置成null。
尽量少用static方法和static成员。
对于不再使用的bitmap应该手动调用recycle方法,并且设置成null。图片还应尽量使用软引用方式,这样可以加快垃圾回收。
LruCache
内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache,这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
为了能够选择一个合适的缓存大小给LruCache, 有以下多个因素应该放入考虑范围内,例如:
1.设备可以为每个应用程序可分配多大的内存。
2.屏幕上一次最多需要显示多少个图片,有多少图片需要进行预加载,因为有可能很快也会显示在屏幕上。
3.屏幕大小和分辨率。一个超高分辨率的设备比起一个较低分辨率的设备,在持有相同数量图片的时候,需要更大的缓存空间。
4.图片的尺寸和大小,还有每张图片会占据多少内存空间。
5.图片被访问的频率有多高。如某些图片的访问频率比其它图片要高,应该使用多个 LruCache 对象来区分不同组的图片。
6.维持好数量和质量之间的平衡。存储多个低像素的图片,而在后台线程加载高像素的图片会更有效。
并没有一个指定的缓存大小可以满足所有的应用程序,应该去分析程序内存的使用情况,然后制定出一个合适的解决方案。
一个太小的缓存空间,有可能造成图片频繁地被释放和重新加载,这并没有好处。而一个太大的缓存空间,则更有可能会引起 java.lang.OutOfMemory 的异常,因为 LruCache 中的强引用不能被释放,而程序又需要内存。
使用LruCache,需要重写sizeOf方法,返回占用的内存大小。下面是一个图片缓存的实现:
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.util.Log; public class BitmapCache { private static final String TAG = "debug"; private LruCache<String, Bitmap> mBitmapCache; public BitmapCache() { // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
long maxSize = Runtime.getRuntime().maxMemory(); Log.d(TAG, "maxMemory size = " + toMB(maxSize)); // LruCache 使用的缓存值,使用系统分配给应用程序大小的 1/8
maxSize = maxSize >> 3;
// maxSize = 1 << 1024 << 1024;
Log.d(TAG, "cache used maxSize = " + toMB(maxSize)); mBitmapCache = new LruCache<String, Bitmap>((int) maxSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
} public void add(String key, Bitmap value) {
mBitmapCache.put(key, value);
} public void remove(String key) {
mBitmapCache.remove(key);
} public Bitmap get(String key) {
return mBitmapCache.get(key);
} public boolean containsKey(String key) {
return mBitmapCache.get(key) != null;
} public static long toMB(long byteOfSize) {
return byteOfSize >> 20;
}
}
使用缓存技术从网络下载图片显示的实例类
BitmapDownloadTask 类从网络下载图片,并将其解析为适配ImageView大小的格式,以减少对内存的占用,并取得一个良好的显示效果。
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.ImageView; import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List; public class BitmapDownloadTask extends AsyncTask<Void, Void, Bitmap> { private static final String TAG = "debug"; private String mImgUrl;
private SoftReference<ImageView> mImageViewSoftReference;
private BitmapCache mBitmapCache;
private List<BitmapDownloadTask> mTaskList;
private int mReqWidth;
private int mReqHeight; public BitmapDownloadTask(BitmapCache bitmapCache, List<BitmapDownloadTask> tasks, ImageView imgView, String url) {
mBitmapCache = bitmapCache;
mTaskList = tasks;
mImageViewSoftReference = new SoftReference<>(imgView);
mImgUrl = url;
mReqWidth = imgView.getMeasuredWidth();
mReqHeight = imgView.getMeasuredHeight();
} @Override
protected Bitmap doInBackground(Void... params) {
Bitmap bitmap;
HttpURLConnection conn = null;
try {
URL url = new URL(mImgUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
bitmap = BitmapTools.decodeSampledBitmapFromInputStream(conn.getInputStream(), mReqWidth, mReqHeight);
Log.d(TAG, "bitmap size: " + bitmap.getWidth() + "/" + bitmap.getHeight() + ", " + bitmap.getConfig()
.name() + ", " + format(bitmap.getByteCount()));
} catch (Exception e) {
Log.e(TAG, "", e);
bitmap = null;
} finally {
if (conn != null)
conn.disconnect();
} return bitmap;
} @Override
protected void onPostExecute(Bitmap bitmap) { mTaskList.remove(this); if (bitmap != null) {
mBitmapCache.add(mImgUrl, bitmap);
} ImageView imgView = mImageViewSoftReference.get();
if (isCancelled() || imgView == null || imgView.getTag() != mImgUrl) {
bitmap = null;
} if (bitmap != null) {
imgView.setImageBitmap(bitmap);
} super.onPostExecute(bitmap);
} private String format(long byteOfSize) {
long KB = byteOfSize >> 10;
long MB = KB >> 10;
if (MB != 0) {
return MB + " MB";
}
return KB + " KB";
}
}
public class BitmapTools { private static final String TAG = "debug"; public static class Size { public Size(int width, int height) {
this.width = width;
this.height = height;
} public void resize(int width, int height) {
this.width = width;
this.height = height;
} public int getWidth() {
return width;
} public int getHeight() {
return height;
} @Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false; Size size = (Size) o; if (width != size.width)
return false;
return height == size.height;
} @Override
public int hashCode() {
int result = width;
result = 31 * result + height;
return result;
} @Override
public String toString() {
return "Size{" + "width=" + width + ", height=" + height + '}';
} private int width;
private int height;
} public static Bitmap decodeSampledBitmapFromPath(String pathName, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, opts);
// Calculate inSampleSize
opts.inSampleSize = calculateInSampleSize(opts, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, opts);
} public static Bitmap decodeSampledBitmapFromData(byte[] data, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(data, 0, data.length, opts);
// Calculate inSampleSize
opts.inSampleSize = calculateInSampleSize(opts, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeByteArray(data, 0, data.length, opts);
} public static Bitmap decodeSampledBitmapFromResource(Resources res, int id, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, id, opts);
// Calculate inSampleSize
opts.inSampleSize = calculateInSampleSize(opts, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, id, opts);
} public static Bitmap decodeSampledBitmapFromInputStream(InputStream is, int reqWidth, int reqHeight) {
Bitmap bitmap = BitmapFactory.decodeStream(is);
if (bitmap == null)
return null;
// Log.d(TAG, "req size: " + reqWidth + ", " + reqHeight);
// Log.d(TAG, "bitmap size: " + bitmap.getWidth() + ", " + bitmap.getHeight());
if (bitmap.getWidth() > reqWidth || bitmap.getHeight() > reqHeight) {
Size srcSize = new Size(bitmap.getWidth(), bitmap.getHeight());
Size reqSize = new Size(reqWidth, reqHeight);
Size newSize = calculateNewSize(srcSize, reqSize);
bitmap = Bitmap.createScaledBitmap(bitmap, newSize.getWidth(), newSize.getHeight(), false);
}
return bitmap;
} public static Size calculateNewSize(Size srcSize, Size reqSize) {
int newWidth;
int newHeight;
if (srcSize.getWidth() > srcSize.getHeight()) {
newWidth = reqSize.getWidth();
newHeight = newWidth * srcSize.getHeight() / srcSize.getWidth();
} else {
newHeight = reqSize.getHeight();
newWidth = newHeight * srcSize.getWidth() / srcSize.getHeight();
}
return new Size(newWidth, newHeight);
} public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both height and width larger than the requested height and
// width.
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
Log.d(TAG, "inSampleSize=" + inSampleSize);
return inSampleSize;
}
}
Activity类,在滑动时取消所有的任务,没有滑动时自动加载可见范围内的所有图片,以避免加载时滑动产生卡顿的问题。
public class LruAcy extends AppCompatActivity { private static final String TAG = "debug"; private BitmapCache mBitmapCache;
private List<BitmapDownloadTask> mBitmapDownloadTasks; private GridView mImageWall;
private BaseAdapter mAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.acy_lru); mBitmapCache = new BitmapCache();
mBitmapDownloadTasks = new ArrayList<>(); mImageWall = (GridView) findViewById(R.id.image_wall); mAdapter = new BaseAdapter(this);
mImageWall.setOnScrollListener(mOnScrollListener);
mImageWall.setAdapter(mAdapter);
} @Override
protected void onDestroy() {
super.onDestroy(); cancelAll();
} private void setImageView(String url, ImageView imgView) {
Bitmap bitmap = mBitmapCache.get(url);
if (bitmap == null) {
imgView.setImageResource(R.mipmap.ic_launcher);
} else {
imgView.setImageBitmap(bitmap);
}
} private void cancelAll() {
for (BitmapDownloadTask task : mBitmapDownloadTasks) {
task.cancel(true);
}
mBitmapDownloadTasks.clear();
} private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
try {
for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
String imageUrl = ImageUrls.IMAGE_URLS[i];
Bitmap bitmap = mBitmapCache.get(imageUrl);
ImageView imageView = (ImageView) mImageWall.findViewWithTag(imageUrl);
if (imageView == null)
continue;
// Log.d(TAG, "bitmap=" + bitmap + ", imageView=" + imageView);
if (bitmap == null) {
BitmapDownloadTask task = new BitmapDownloadTask(mBitmapCache, mBitmapDownloadTasks, imageView,
imageUrl);
mBitmapDownloadTasks.add(task);
task.execute();
} else {
imageView.setImageBitmap(bitmap);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} private AbsListView.OnScrollListener mOnScrollListener = new AbsListView.OnScrollListener() { private boolean mIsFirstRun = true;
private int firstVisibleItem;
private int visibleItemCount; @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
loadBitmaps(firstVisibleItem, visibleItemCount);
} else {
cancelAll();
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.firstVisibleItem = firstVisibleItem;
this.visibleItemCount = visibleItemCount;
if (mIsFirstRun && totalItemCount > 0) {
mIsFirstRun = false;
loadBitmaps(firstVisibleItem, visibleItemCount);
}
}
}; private class BaseAdapter extends android.widget.BaseAdapter { private LayoutInflater mInflater; public BaseAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} @Override
public int getCount() {
return ImageUrls.IMAGE_URLS.length;
} @Override
public String getItem(int position) {
return ImageUrls.IMAGE_URLS[position];
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) {
convertView = mInflater.inflate(R.layout.image_layout, parent, false);
} String url = getItem(position);
ImageView img = (ImageView) convertView.findViewById(R.id.img);
img.setTag(url);
setImageView(url, img); return convertView;
}
}
}
界面布局 acy_lru.xml image_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.john.webapp.LruAcy"> <GridView
android:id="@+id/image_wall"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:horizontalSpacing="5dp"
android:numColumns="4"
android:verticalSpacing="5dp"/> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <ImageView
android:id="@+id/img"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitXY"/> </LinearLayout>
测试使用的Url
public class ImageUrls { public final static String[] IMAGE_URLS = new String[]{
"http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037194_2965.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037193_1687.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037193_1286.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037192_8379.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037178_9374.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037177_1254.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037177_6203.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037152_6352.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037151_9565.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037148_7104.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037129_8825.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037128_5291.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037128_3531.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037127_1085.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037095_7515.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037094_8001.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037093_7168.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037091_4950.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949643_6410.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949642_6939.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949630_4505.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949630_4593.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949629_7309.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949629_8247.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949615_1986.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_8482.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_3743.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_4199.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949599_3416.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949599_5269.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949598_7858.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949598_9982.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949578_2770.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949578_8744.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949577_5210.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949577_1998.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949482_8813.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949481_6577.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949480_4490.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949455_6792.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949455_6345.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4553.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949441_8987.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949441_5454.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949454_6367.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg"
};
}
LruCache:从网络加载图片缓存实例的更多相关文章
- iOS网络加载图片缓存策略之ASIDownloadCache缓存优化
iOS网络加载图片缓存策略之ASIDownloadCache缓存优化 在我们实际工程中,很多情况需要从网络上加载图片,然后将图片在imageview中显示出来,但每次都要从网络上请求,会严重影响用 ...
- iOS网络加载图片缓存与SDWebImage
加载网络图片可以说是网络应用中必备的.如果单纯的去下载图片,而不去做多线程.缓存等技术去优化,加载图片时的效果与用户体验就会很差. 一.自己实现加载图片的方法 tips: *iOS中所有网络访问都是异 ...
- 【转】Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址
Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址 关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddr ...
- swift 基础小结01 --delegate、Optional、GCD的使用、request请求、网络加载图片并保存到沙箱、闭包以及桥接
本文主要记录swift中delegate的使用.“?!”Optional的概念.GCD的使用.request请求.网络加载图片并保存到沙箱.闭包以及桥接. 一.delegate的使用 swift中de ...
- android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存
经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目 Android-Universal-Image-Loader 或者 ignition 都是个很好的选择. 在这里把原来 ...
- Android简易实战教程--第三十二话《使用Lrucache和NetworkImageView加载图片》
转载本专栏每一篇博客请注明转载出处地址,尊重原创.此博客转载链接地址:小杨的博客 http://blog.csdn.net/qq_32059827/article/details/5279131 ...
- Glide加载图片缓存库出现——You cannot start a load for a destroyed activity
请记住一句话:不要再非主线程里面使用Glide加载图片,如果真的使用了,请把context参数换成getApplicationContext.
- 使用自定义的item、Adapter和AsyncTask、第三方开源框架PullToRefresh联合使用实现自定义的下拉列表(从网络加载图片显示在item中的ImageView)
AsyncTask使用方法详情:http://www.cnblogs.com/zzw1994/p/4959949.html 下拉开源框架PullToRefresh使用方法和下载详情:http://ww ...
- 开启AsyncTask从网络加载图片
/*AsyncTask 异步任务即做一些简单的异步处理 :是handle与线程池的封装 * 第一个泛型:参数类型泛型 * 第二个泛型:更新进度泛型 * 第三个泛型:onProgressUpdate的返 ...
随机推荐
- (python)leetcode刷题笔记05 Longest Palindromic Substring
5. Longest Palindromic Substring Given a string s, find the longest palindromic substring in s. You ...
- 在 Ubuntu 下安装 Deepin 的 QQ、微信、百度云和迅雷等软件
在以前的文章 Ubuntu 常用软件推荐(QQ.微信.MATLAB等)及安装过程 中,我们用 Wine QQ 和 Electronic Wechat 来解决 Ubuntu 系统下使用 QQ 和微信的难 ...
- dice2win早期版本
原理; https://medium.com/dapppub/fairdicedesign-315a4e253ad6 早期版本地址: https://etherscan.io/address/0xD1 ...
- ServiceStack.Ormlit sqlserver枚举类型映射字段类型为varchar
请当枚举类型上面加上[Flags]特性就可以了.
- Memory及其controller芯片整体测试方案(下篇)
{ 第三部分 } DDR总线的设计.调试和验证 在计算机架构中,DDR作为程序运算的动态存储器,面对如高性能计算.图形计算.移动计算.工业应用等领域的要求,发展出DDR4,以及用于图形计算的G ...
- 接口文档管理工具-Postman、Swagger、RAP(转载)
接口文档管理工具-Postman.Swagger.RAP 转自:http://www.51testing.com/html/10/n-3715910.html 在项目开发测试中,接口文档是贯穿始终的. ...
- TCP系列23—重传—13、RACK重传
一.RACK概述 RACK(Recent ACKnowledgment)是一种新的基于时间的丢包探测算法,RACK的目的是取代传统的基于dupthresh门限的各种快速重传及其变种.前面介绍的各种基于 ...
- html中图片自适应浏览器和屏幕,宽度高度自适应
1.(宽度自适应):在网页代码的头部,加入一行viewport元标签. <meta name="viewport" content="width=device-wi ...
- Git命令基本操作
本文从以下九个方面,介绍Git命令的基本操作: 一. Git安装 二. Git基本配置 三. 创建Git仓库 四. 获得Git仓库 五. 提交更新 六. 提交历史查看 七. 远程仓库 八. 打Tags ...
- Jenkins系列-Jenkins添加git密钥对
添加密钥 1.添加git用户和git密码对 ,用于git客户端从gitlab上拉取代码到本地