热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android照片墙完整版,完美结合LruCache和DiskLruCache

转载请注明出处:http:blog.csdn.netguolin_blogarticledetails34093441在上一篇文章当中,我们学习了DiskLruCache的概念和基本用

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/34093441


在上一篇文章当中,我们学习了DiskLruCache的概念和基本用法,但仅仅是掌握理论知识显然是不够的,那么本篇文章我们就来继续进阶一下,看一看在实战当中应该怎样合理使用DiskLruCache。还不熟悉DiskLruCache用法的朋友可以先去参考我的上一篇文章 Android DiskLruCache完全解析,硬盘缓存的最佳方案 。


其实,在真正的项目实战当中如果仅仅是使用硬盘缓存的话,程序是有明显短板的。而如果只使用内存缓存的话,程序当然也会有很大的缺陷。因此,一个优秀的程序必然会将内存缓存和硬盘缓存结合到一起使用,那么本篇文章我们就来看一看,如何才能将LruCache和DiskLruCache完美结合到一起。


Android照片墙应用实现,再多的图片也不怕崩溃 这篇文章当中,我编写了一个照片墙的应用程序,但当时只是单纯使用到了内存缓存而已,而今天我们就对这个例子进行扩展,制作一个完整版的照片墙。


那我们开始动手吧,新建一个Android项目,起名叫PhotoWallDemo,这里我使用的是Android 4.0的API。然后新建一个libcore.io包,并将DiskLruCache.java文件拷贝到这个包下,这样就把准备工作完成了。


接下来首先需要考虑的仍然是图片源的问题,简单起见,我仍然是吧所有图片都上传到了我的CSDN相册当中,然后新建一个Images类,将所有相册中图片的网址都配置进去,代码如下所示:

public class Images {
public final static String[] imageThumbUrls = new String[] {
"http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383291_6518.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383291_8239.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383290_9329.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383290_1042.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383275_3977.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383264_3954.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383264_4787.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383264_8243.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383248_3693.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383243_5120.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383242_9576.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383242_1721.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383219_5806.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383214_7794.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383213_4418.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383213_3557.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383210_8779.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383172_4577.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383166_3407.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383166_2224.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383166_7301.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383165_7197.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383150_8410.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383131_3736.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383130_5094.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383130_7393.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383129_8813.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383100_3554.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383093_7894.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383092_2432.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383092_3071.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383091_3119.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383059_6589.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383059_8814.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383059_2237.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383058_4330.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406383038_3602.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382942_3079.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382942_8125.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382942_4881.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382941_4559.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382941_3845.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382924_8955.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382923_2141.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382923_8437.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382922_6166.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382922_4843.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382905_5804.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382904_3362.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382904_2312.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382904_4960.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382900_2418.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382881_4490.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382881_5935.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382880_3865.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382880_4662.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382879_2553.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382862_5375.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382862_1748.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382861_7618.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382861_8606.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382861_8949.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382841_9821.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382840_6603.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382840_2405.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382840_6354.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382839_5779.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382810_7578.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382810_2436.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382809_3883.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382809_6269.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382808_4179.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382790_8326.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382789_7174.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382789_5170.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382789_4118.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382788_9532.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382767_3184.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382767_4772.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382766_4924.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382766_5762.jpg",
"http://img.my.csdn.net/uploads/201407/26/1406382765_7341.jpg"
};
}
设置好了图片源之后,我们需要一个GridView来展示照片墙上的每一张图片。打开或修改activity_main.xml中的代码,如下所示:
        
很简单,只是在LinearLayout中写了一个GridView而已。接着我们要定义GridView中每一个子View的布局,新建一个photo_layout.xml布局,加入如下代码:
    

仍然很简单,photo_layout.xml布局中只有一个ImageView控件,就是用它来显示图片的。这样我们就把所有的布局文件都写好了。


接下来新建PhotoWallAdapter做为GridView的适配器,代码如下所示:

public class PhotoWallAdapter extends ArrayAdapter {	/**	 * 记录所有正在下载或等待下载的任务。	 */	private Set taskCollection;	/**	 * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。	 */	private LruCache mMemoryCache;	/**	 * 图片硬盘缓存核心类。	 */	private DiskLruCache mDiskLruCache;	/**	 * GridView的实例	 */	private GridView mPhotoWall;	/**	 * 记录每个子项的高度。	 */	private int mItemHeight = 0;	public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,			GridView photoWall) {		super(context, textViewResourceId, objects);		mPhotoWall = photoWall;		taskCollection = new HashSet();		// 获取应用程序最大可用内存		int maxMemory = (int) Runtime.getRuntime().maxMemory();		int cacheSize = maxMemory / 8;		// 设置图片缓存大小为程序最大可用内存的1/8		mMemoryCache = new LruCache(cacheSize) {			@Override			protected int sizeOf(String key, Bitmap bitmap) {				return bitmap.getByteCount();			}		};		try {			// 获取图片缓存路径			File cacheDir = getDiskCacheDir(context, "thumb");			if (!cacheDir.exists()) {				cacheDir.mkdirs();			}			// 创建DiskLruCache实例,初始化缓存数据			mDiskLruCache = DiskLruCache					.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);		} catch (IOException e) {			e.printStackTrace();		}	}	@Override	public View getView(int position, View convertView, ViewGroup parent) {		final String url = getItem(position);		View view;		if (cOnvertView== null) {			view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);		} else {			view = convertView;		}		final ImageView imageView = (ImageView) view.findViewById(R.id.photo);		if (imageView.getLayoutParams().height != mItemHeight) {			imageView.getLayoutParams().height = mItemHeight;		}		// 给ImageView设置一个Tag,保证异步加载图片时不会乱序		imageView.setTag(url);		imageView.setImageResource(R.drawable.empty_photo);		loadBitmaps(imageView, url);		return view;	}	/**	 * 将一张图片存储到LruCache中。	 * 	 * @param key	 *            LruCache的键,这里传入图片的URL地址。	 * @param bitmap	 *            LruCache的键,这里传入从网络上下载的Bitmap对象。	 */	public void addBitmapToMemoryCache(String key, Bitmap bitmap) {		if (getBitmapFromMemoryCache(key) == null) {			mMemoryCache.put(key, bitmap);		}	}	/**	 * 从LruCache中获取一张图片,如果不存在就返回null。	 * 	 * @param key	 *            LruCache的键,这里传入图片的URL地址。	 * @return 对应传入键的Bitmap对象,或者null。	 */	public Bitmap getBitmapFromMemoryCache(String key) {		return mMemoryCache.get(key);	}	/**	 * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,	 * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。	 */	public void loadBitmaps(ImageView imageView, String imageUrl) {		try {			Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);			if (bitmap == null) {				BitmapWorkerTask task = new BitmapWorkerTask();				taskCollection.add(task);				task.execute(imageUrl);			} else {				if (imageView != null && bitmap != null) {					imageView.setImageBitmap(bitmap);				}			}		} catch (Exception e) {			e.printStackTrace();		}	}	/**	 * 取消所有正在下载或等待下载的任务。	 */	public void cancelAllTasks() {		if (taskCollection != null) {			for (BitmapWorkerTask task : taskCollection) {				task.cancel(false);			}		}	}	/**	 * 根据传入的uniqueName获取硬盘缓存的路径地址。	 */	public File getDiskCacheDir(Context context, String uniqueName) {		String cachePath;		if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())				|| !Environment.isExternalStorageRemovable()) {			cachePath = context.getExternalCacheDir().getPath();		} else {			cachePath = context.getCacheDir().getPath();		}		return new File(cachePath + File.separator + uniqueName);	}	/**	 * 获取当前应用程序的版本号。	 */	public int getAppVersion(Context context) {		try {			PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(),					0);			return info.versionCode;		} catch (NameNotFoundException e) {			e.printStackTrace();		}		return 1;	}	/**	 * 设置item子项的高度。	 */	public void setItemHeight(int height) {		if (height == mItemHeight) {			return;		}		mItemHeight = height;		notifyDataSetChanged();	}	/**	 * 使用MD5算法对传入的key进行加密并返回。	 */	public String hashKeyForDisk(String key) {		String cacheKey;		try {			final MessageDigest mDigest = MessageDigest.getInstance("MD5");			mDigest.update(key.getBytes());			cacheKey = bytesToHexString(mDigest.digest());		} catch (NoSuchAlgorithmException e) {			cacheKey = String.valueOf(key.hashCode());		}		return cacheKey;	}		/**	 * 将缓存记录同步到journal文件中。	 */	public void fluchCache() {		if (mDiskLruCache != null) {			try {				mDiskLruCache.flush();			} catch (IOException e) {				e.printStackTrace();			}		}	}	private String bytesToHexString(byte[] bytes) {		StringBuilder sb = new StringBuilder();		for (int i = 0; i  {		/**		 * 图片的URL地址		 */		private String imageUrl;		@Override		protected Bitmap doInBackground(String... params) {			imageUrl = params[0];			FileDescriptor fileDescriptor = null;			FileInputStream fileInputStream = null;			Snapshot snapShot = null;			try {				// 生成图片URL对应的key				final String key = hashKeyForDisk(imageUrl);				// 查找key对应的缓存				snapShot = mDiskLruCache.get(key);				if (snapShot == null) {					// 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存					DiskLruCache.Editor editor = mDiskLruCache.edit(key);					if (editor != null) {						OutputStream outputStream = editor.newOutputStream(0);						if (downloadUrlToStream(imageUrl, outputStream)) {							editor.commit();						} else {							editor.abort();						}					}					// 缓存被写入后,再次查找key对应的缓存					snapShot = mDiskLruCache.get(key);				}				if (snapShot != null) {					fileInputStream = (FileInputStream) snapShot.getInputStream(0);					fileDescriptor = fileInputStream.getFD();				}				// 将缓存数据解析成Bitmap对象				Bitmap bitmap = null;				if (fileDescriptor != null) {					bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);				}				if (bitmap != null) {					// 将Bitmap对象添加到内存缓存当中					addBitmapToMemoryCache(params[0], bitmap);				}				return bitmap;			} catch (IOException e) {				e.printStackTrace();			} finally {				if (fileDescriptor == null && fileInputStream != null) {					try {						fileInputStream.close();					} catch (IOException e) {					}				}			}			return null;		}		@Override		protected void onPostExecute(Bitmap bitmap) {			super.onPostExecute(bitmap);			// 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。			ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);			if (imageView != null && bitmap != null) {				imageView.setImageBitmap(bitmap);			}			taskCollection.remove(this);		}		/**		 * 建立HTTP请求,并获取Bitmap对象。		 * 		 * @param imageUrl		 *            图片的URL地址		 * @return 解析后的Bitmap对象		 */		private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {			HttpURLConnection urlCOnnection= null;			BufferedOutputStream out = null;			BufferedInputStream in = null;			try {				final URL url = new URL(urlString);				urlCOnnection= (HttpURLConnection) url.openConnection();				in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);				out = new BufferedOutputStream(outputStream, 8 * 1024);				int b;				while ((b = in.read()) != -1) {					out.write(b);				}				return true;			} catch (final IOException e) {				e.printStackTrace();			} finally {				if (urlConnection != null) {					urlConnection.disconnect();				}				try {					if (out != null) {						out.close();					}					if (in != null) {						in.close();					}				} catch (final IOException e) {					e.printStackTrace();				}			}			return false;		}	}}

代码有点长,我们一点点进行分析。首先在PhotoWallAdapter的构造函数中,我们初始化了LruCache类,并设置了内存缓存容量为程序最大可用内存的1/8,紧接着调用了DiskLruCache的open()方法来创建实例,并设置了硬盘缓存容量为10M,这样我们就把LruCache和DiskLruCache的初始化工作完成了。


接着在getView()方法中,我们为每个ImageView设置了一个唯一的Tag,这个Tag的作用是为了后面能够准确地找回这个ImageView,不然异步加载图片会出现乱序的情况。然后在getView()方法的最后调用了loadBitmaps()方法,加载图片的具体逻辑也就是在这里执行的了。


进入到loadBitmaps()方法中可以看到,实现是调用了getBitmapFromMemoryCache()方法来从内存中获取缓存,如果获取到了则直接调用ImageView的setImageBitmap()方法将图片显示到界面上。如果内存中没有获取到,则开启一个BitmapWorkerTask任务来去异步加载图片。


那么在BitmapWorkerTask的doInBackground()方法中,我们就灵活运用了上篇文章中学习的DiskLruCache的各种用法。首先根据图片的URL生成对应的MD5 key,然后调用DiskLruCache的get()方法来获取硬盘缓存,如果没有获取到的话则从网络上请求图片并写入硬盘缓存,接着将Bitmap对象解析出来并添加到内存缓存当中,最后将这个Bitmap对象显示到界面上,这样一个完整的流程就执行完了。


那么我们再来分析一下上述流程,每次加载图片的时候都优先去内存缓存当中读取,当读取不到的时候则回去硬盘缓存中读取,而如果硬盘缓存仍然读取不到的话,就从网络上请求原始数据。不管是从硬盘缓存还是从网络获取,读取到了数据之后都应该添加到内存缓存当中,这样的话我们下次再去读取图片的时候就能迅速从内存当中读取到,而如果该图片从内存中被移除了的话,那就重复再执行一遍上述流程就可以了。


这样我们就把LruCache和DiskLruCache完美结合到一起了。接下来还需要编写MainActivity的代码,非常简单,如下所示:

public class MainActivity extends Activity {	/**	 * 用于展示照片墙的GridView	 */	private GridView mPhotoWall;	/**	 * GridView的适配器	 */	private PhotoWallAdapter mAdapter;	private int mImageThumbSize;	private int mImageThumbSpacing;	@Override	protected void onCreate(Bundle savedInstanceState) {		super.onCreate(savedInstanceState);		setContentView(R.layout.activity_main);		mImageThumbSize = getResources().getDimensionPixelSize(				R.dimen.image_thumbnail_size);		mImageThumbSpacing = getResources().getDimensionPixelSize(				R.dimen.image_thumbnail_spacing);		mPhotoWall = (GridView) findViewById(R.id.photo_wall);		mAdapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls,				mPhotoWall);		mPhotoWall.setAdapter(mAdapter);		mPhotoWall.getViewTreeObserver().addOnGlobalLayoutListener(				new ViewTreeObserver.OnGlobalLayoutListener() {										@Override					public void onGlobalLayout() {						final int numColumns = (int) Math.floor(mPhotoWall								.getWidth()								/ (mImageThumbSize + mImageThumbSpacing));						if (numColumns > 0) {							int columnWidth = (mPhotoWall.getWidth() / numColumns)									- mImageThumbSpacing;							mAdapter.setItemHeight(columnWidth);							mPhotoWall.getViewTreeObserver()									.removeGlobalOnLayoutListener(this);						}					}				});	}		@Override	protected void onPause() {		super.onPause();		mAdapter.fluchCache();	}	@Override	protected void onDestroy() {		super.onDestroy();		// 退出程序时结束所有的下载任务		mAdapter.cancelAllTasks();	}}

上述代码中,我们通过getViewTreeObserver()的方式监听View的布局事件,当布局完成以后,我们重新修改一下GridView中子View的高度,以保证子View的宽度和高度可以保持一致。


到这里还没有结束,最后还需要配置一下AndroidManifest.xml文件,并加入相应的权限,如下所示:

                                                                                            

好了,全部代码都在这儿了,让我们来运行一下吧,效果如下图所示:




第一次从网络上请求图片的时候有点慢,但之后加载图片就会非常快了,滑动起来也很流畅。


那么我们最后再检查一下这些图片是不是已经正确缓存在指定地址了,进入 /sdcard/Android/data//cache/thumb 这个路径,如下图所示:




可以看到,每张图片的缓存以及journal文件都在这里了,说明我们的硬盘缓存已经成功了。

好了,今天的讲解就到这里,有疑问的朋友可以在下面留言。


源码下载,请点击这里


关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

微信扫一扫下方二维码即可关注:

        


推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
author-avatar
荆灬樊小姐_526
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有