Android优化双缓存的图片异步加载工具(LruCache+SoftReference)

 更新时间:2016年9月20日 19:58  点击:2072
下面来为各位介绍一个关于Android优化双缓存的图片异步加载工具(LruCache+SoftReference)例子,希望对大家有帮助.


之前在郭大神的博客看到使用LruCache算法实现图片缓存的.这里仿效他的思路,自己也写了一个. 并加入ConcurrentHashMap<String, SoftReference<Bitmap>>去实现二级缓存,因为ConcurrentHashMap是多个锁的线程安全,支持高并发.很适合这种频繁访问读取内存的操作.


下面整个思路是,使用了系统提供的LruCache类做一级缓存, 大小为运行内存的1/8,当LruCache容量要满的时候,会自动将系统移除的图片放到二级缓存中,但为了避免OOM的问题,这里将SoftReference软引用加入来,当系统快要OOM的时候会自动清除里面的图片内存,当然内存充足时就会继续保存这些二级缓存的图片.强调一点,不要用SoftReference去做一级缓存,现在的java中垃圾回收加强了对SoftReference软引用的回收机制,它只适合临时的保存一些数据缓存,并不适合长期的(相对临时而言,并不是真正的长期).


直接上代码,拿来即用哦:

/**
 * Created on 3/11/2015
 * <br>图片异步加载工具(支持本地图片加载,网络图片URL和项目内图片资源加载)
 * <br>支持双缓存: LruCache和SoftReference
 * @author Mr.Et
 *
 */
public class ImageLoadManager {
 /** 图片源类型: 文件,网络,资源ID **/
 public enum IMAGE_LOAD_TYPE
 {
  FILE_PATH,FILE_URL,FILE_RESOURCE_ID
 }
 
 private String TAG = "ImageLoadManager...";
 
 private Context context;
 
 private Set<ImageLoadTask> taskCollection;
 
 /** 最大内存 **/
 final static int maxCacheSize = (int)(Runtime.getRuntime().maxMemory() / 8);
 
 /** 建立线程安全,支持高并发的容器 **/
 private static ConcurrentHashMap<String, SoftReference<Bitmap>> currentHashmap
  = new ConcurrentHashMap<String, SoftReference<Bitmap>>();
 
 
 
 
 
 
 
 public ImageLoadManager(Context context)
 {
  super();
  this.context = context;
  taskCollection = new HashSet<ImageLoadManager.ImageLoadTask>();
 }
 
 private static LruCache<String, Bitmap> BitmapMemoryCache = new LruCache<String, Bitmap>(maxCacheSize)
 {
  @Override
  protected int sizeOf(String key, Bitmap value)
  {
   if(value != null)
   {
    return value.getByteCount();
    //return value.getRowBytes() * value.getHeight(); //旧版本的方法
   }
   else
   {
    return 0;
   }
  }
  
  //这个方法当LruCache的内存容量满的时候会调用,将oldValue的元素移除出来腾出空间给新的元素加入
  @Override
  protected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue)
  {
   if(oldValue != null)
   {
    // 当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存   
    currentHashmap.put(key, new SoftReference<Bitmap>(oldValue));
   }
  }
  
 };
 
 /**
  * 针对提供图片资源ID来显示图片的方法
  * @param loadType 图片加载类型
  * @param imageResourceID 图片资源id
  * @param imageView 显示图片的ImageView
  */
 public void setImageView(IMAGE_LOAD_TYPE loadType, int imageResourceID, ImageView imageView)
 {
  if(loadType == IMAGE_LOAD_TYPE.FILE_RESOURCE_ID)
  {
//   if(ifResourceIdExist(imageResourceID))
//   {
//    imageView.setImageResource(imageResourceID);
//    
//   }else{ //映射无法获取该图片,则显示默认图片
//    imageView.setImageResource(R.drawable.pic_default);
//   }
   try
   {
    imageView.setImageResource(imageResourceID);
    return;
   } catch (Exception e) {
    Log.e(TAG, "Can find the imageID of "+imageResourceID);
    e.printStackTrace();
   }
   //默认图片
   imageView.setImageResource(R.drawable.pic_default);
  }
 }
 
 /**
  * 针对提供图片文件链接或下载链接来显示图片的方法
  * @param loadType  图片加载类型
  * @param imageFilePath 图片文件的本地文件地址或网络URL的下载链接
  * @param imageView 显示图片的ImageView
  */
 public void setImageView(IMAGE_LOAD_TYPE loadType, String imageFilePath, ImageView imageView)
 {
  if(imageFilePath == null || imageFilePath.trim().equals(""))
  {
   imageView.setImageResource(R.drawable.pic_default);
   
  }else{
   Bitmap bitmap = getBitmapFromMemoryCache(imageFilePath);
   if(bitmap != null)
   {
    imageView.setImageBitmap(bitmap);
   }
   else
   {
    imageView.setImageResource(R.drawable.pic_default);
    ImageLoadTask task = new ImageLoadTask(loadType, imageView);
    taskCollection.add(task);
    task.execute(imageFilePath);
   }
  }
 }
 
 /**
  * 从LruCache中获取一张图片,如果不存在就返回null
  * @param key  键值可以是图片文件的filePath,可以是图片URL地址
  * @return Bitmap对象,或者null
  */
 public Bitmap getBitmapFromMemoryCache(String key)
 { 
  try
  {
   if(BitmapMemoryCache.get(key) == null)
   {
    if(currentHashmap.get(key) != null)
    {
     return currentHashmap.get(key).get();
    }
   }
   return BitmapMemoryCache.get(key);
   
  } catch (Exception e) {
   e.printStackTrace();
  }
  return BitmapMemoryCache.get(key);
 }
 
 /**
  * 将图片放入缓存
  * @param key
  * @param bitmap
  */
 private void addBitmapToCache(String key, Bitmap bitmap)
 {
  BitmapMemoryCache.put(key, bitmap);
 }
 
 
 /**
  * 图片异步加载
  * @author Mr.Et
  *
  */
 private class ImageLoadTask extends AsyncTask<String, Void, Bitmap>
 {
  private String imagePath;
  private ImageView imageView;
  private IMAGE_LOAD_TYPE loadType;
  
  public ImageLoadTask(IMAGE_LOAD_TYPE loadType , ImageView imageView)
  {
   this.loadType = loadType;
   this.imageView = imageView;
  }
  
  @Override
  protected Bitmap doInBackground(String...params)
  {
   imagePath = params[0];
   try
   {
    if(loadType == IMAGE_LOAD_TYPE.FILE_PATH)
    {
     if(new File(imagePath).exists())
     { //从本地FILE读取图片
      BitmapFactory.Options opts = new BitmapFactory.Options();
      opts.inSampleSize = 2;
      Bitmap bitmap = BitmapFactory.decodeFile(imagePath, opts);
      //将获取的新图片放入缓存
      addBitmapToCache(imagePath, bitmap);
      return bitmap;
     }
     return null;
    }
    else if(loadType == IMAGE_LOAD_TYPE.FILE_URL)
    { //从网络下载图片
     byte[] datas = getBytesOfBitMap(imagePath);
     if(datas != null)
     {
//      BitmapFactory.Options opts = new BitmapFactory.Options();
//      opts.inSampleSize = 2;
//      Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, opts);
      Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length);
      addBitmapToCache(imagePath, bitmap);
      return bitmap;
     }
     return null;
    }
    
   } catch (Exception e) {
    e.printStackTrace();
    FileUtils.saveExceptionLog(e);
    //可自定义其他操作
   }
   return null;
  }
  
  @Override
  protected void onPostExecute(Bitmap bitmap)
  {
   try
   {
    if(imageView != null)
    {
     if(bitmap != null)
     {
      imageView.setImageBitmap(bitmap);
     }
     else
     {
      Log.e(TAG, "The bitmap result is null...");
     }
    }
    else
    {
     Log.e(TAG, "The imageView is null...");
     //获取图片失败时显示默认图片
     imageView.setImageResource(R.drawable.pic_default);
    }
    
   } catch (Exception e) {
    e.printStackTrace();
    FileUtils.saveExceptionLog(e);
   }
  }
  
  
 }
 
 
 /**
  * InputStream转byte[]
  * @param inStream
  * @return
  * @throws Exception
  */
 private byte[] readStream(InputStream inStream) throws Exception{ 
        ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 
        byte[] buffer = new byte[2048]; 
        int len = 0; 
        while( (len=inStream.read(buffer)) != -1){ 
            outStream.write(buffer, 0, len); 
        } 
        outStream.close(); 
        inStream.close(); 
        return outStream.toByteArray(); 
    }
 
 /**
  * 获取下载图片并转为byte[]
  * @param urlStr
  * @return
  */
 private byte[] getBytesOfBitMap(String imgUrl){
  try {
   URL url = new URL(imgUrl);
   HttpURLConnection conn = (HttpURLConnection) url.openConnection();
   conn.setConnectTimeout(10 * 1000);  //10s
   conn.setReadTimeout(20 * 1000);
         conn.setRequestMethod("GET"); 
         conn.connect();
         InputStream in = conn.getInputStream();
         return readStream(in);
  } catch (IOException e) {
   e.printStackTrace();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }
 
 /**
  * 该资源ID是否有效
  * @param resourceId 资源ID
  * @return
  */
 private boolean ifResourceIdExist(int resourceId)
 {
  try
  {
   Field field = R.drawable.class.getField(String.valueOf(resourceId));
   Integer.parseInt(field.get(null).toString());
   return true;
   
  } catch (Exception e) {
   e.printStackTrace();
  }
  return false;
 }
 
 /**
  * 取消所有任务
  */
 public void cancelAllTask()
 {
  if(taskCollection != null){
   for(ImageLoadTask task : taskCollection)
   {
    task.cancel(false);
   }
  }
 }
 
 
}

In addition, 如果需要更加完美的体验,还可以加入第三级的缓存机制, 比如将图片缓存到本地的磁盘存储空间中.但是又不想这些缓存在本地的图片被其他应用扫描到或者被用户看到怎么办? 这里有几个思路, 比如将图片用加密算法转为字符串存储,或者将图片转为自定义格式的未知文件去放在隐蔽的地方(很多应用都采取了这种方式). 这个不妨自己去尝试实现哦~

在64位的Ubuntu上运行android sdk遇到“-bash: ./emulator: No such file or directory”的错误提示,发现是64位的Ubuntu系统上刚好缺少所依赖的32位的库。

前几天在Xen或者KVM的guest上运行Android emulator来模拟Android系统的运行。当时是使用64位的Ubuntu 12.10系统作为guest,在其中运行emulator或emulator-x86时遇到“-bash: ./emulator: No such file or directory”的错误提示,这个错误提示的有点莫名其妙,很难看明白到底发生了什么情况。后来,我发现是由于这两个二进制文件是32bit的它会依赖一些32bit的共享库文件,而该64位Ubuntu系统上刚好缺少所需的32位的库。所以,这个问题的解决方法是用“sudo apt-get install ia32-libs”命令安装32位的库。另外可以用“ldd emulator”命令查看一下,emulator执行文件说依赖的共享库有哪些,可能会出现一些是“not found”的情况,然后依次安装相应的共享库即可。

 代码如下 复制代码
linux@Ubuntu12.10:~/android-sdk-linux/tools$ ./emulator-x86 -avd test1
-bash: ./emulator-x86: No such file or directory
linux@Ubuntu12.10:~/android-sdk-linux/tools$ ./emulator -avd test1
-bash: ./emulator: No such file or directory
 
linux@Ubuntu12.10:~/android-sdk-linux/tools$ sudo apt-get install ia32-libs
 
linux@Ubuntu12.10:~/android-sdk-linux/tools$ ldd emulator
        linux-gate.so.1 =>  (0xf7776000)
        libutil.so.1 => /lib/i386-linux-gnu/libutil.so.1 (0xf7757000)
        librt.so.1 => /lib/i386-linux-gnu/librt.so.1 (0xf774e000)
        libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0xf7732000)
        libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0xf7649000)
        libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf761d000)
        libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0xf75ff000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7455000)
        /lib/ld-linux.so.2 (0xf7777000)


另外,使用KVM加速的Android emulator在x86平台上用起来还是真不错的。顺手记几个命令吧,下次如果使用时,我也可以方便参考。

 代码如下 复制代码
linux@Ubuntu12.10:~/android-sdk-linux/tools$ ./android list targets
Available Android targets:
----------
............
----------
id: 3 or "android-16"
     Name: Android 4.1
     Type: Platform
     API level: 16
     Revision: 2
     Skins: HVGA, WXGA800, WSVGA, WXGA800-7in, WQVGA432, QVGA, WVGA800 (default), WVGA854, WXGA720, WQVGA400
     ABIs : armeabi-v7a, x86
 
linux@Ubuntu12.10:~/android-sdk-linux/tools$ ./android create avd -n test1 -t 3 -b x86
 
linux@Ubuntu12.10:~/android-sdk-linux/tools$ ./emulator-x86 -avd test1 -qemu -m 1024 -enable-kvm


关于Android emulator的参考参考资料:
Android SDK下载地址:http://developer.android.com/sdk/index.html#download
emulator的使用:http://developer.android.com/tools/devices/emulator.html
命令行管理AVD文件:http://developer.android.com/tools/devices/managing-avds-cmdline.html

本文来分享我在Ubuntu使用Android SDK开发经验的几点总结,测试环境为:Ubuntu 12.04 x86_64,ADT Bundle Linux x86_64 。

测试硬件环境:

打开了Intel VT的PC (使用KVM时需要VT支持的)

测试软件环境:

Ubuntu 12.04 x86_64

ADT Bundle Linux x86_64 (在android官网下载:https://developer.android.com/sdk/index.html )

1. 下载所需的Image和创建AVD:

可以在Eclipse(ADT)中,”Window” -> “Andorid SDK Manager”来打开SDK管理器进行下载,需要下载相应版本(如Android 4.2.2/4.3)的SDK Platform及其对应的ARM或Intel x86 Atom的系统镜像(如果要在Intel PC上使用Emulator并且要使用KVM加速,则一定需要下载Intel x86 Atom的系统镜像)。另外,由于SDK Manager是默认到google.com的一些网站下载相关的信息,如果发现有连接不上的情况(原因你懂的),可以通过”Tools”->”Options”中设置你自己的代理。

在Eclipse-ADT中,”Window” -> “Andorid Virtual Device Manager” 来打开AVD管理器,在上面可以创建自己的AVD文件,注意:如果要在Intel平台上使用KVM加速,则创建AVD时,对CPU/ABI的选择是 “Intel Atom (x86)”。

另外,如果使用一些程序时,其定义的API没有找到,就需要自己下载了;比如,当前有Android API 18 SDK,可能某个程序需要API 17,就需要下载Android 17的SDK了。

2. KVM加速:

我们都知道,运行Android Emulator时很慢的,有时慢到难以忍受。

如果在Android Emulaor中使用了KVM,则可以实现启动和运行时程序的加速,效果可能提高5~10倍的执行速度;记得用x86那种AVD。

一些命令行操作如下:

 代码如下 复制代码
jay@jay-linux:~$ sudo modprobe kvm
jay@jay-linux:~$ sudo modprobe kvm_intel
 
jay@jay-linux:~$ lsmod | grep kvm
kvm_intel             137721  0
kvm                   415550  1 kvm_intel
 
jay@jay-linux:~$ ps -ef | grep emulator | grep -v grep
jay      11749 10704 14 19:48 pts/18   00:02:18 /home/jay/adt-bundle-linux-x86_64-20130917/sdk//tools/emulator64-x86 -avd Android-4.2-x86 -scale 0.23
 
jay@jay-linux:~$ lsmod | grep kvm
kvm_intel             137721  3
kvm                   415550  1 kvm_intel
# 可看使用Emualtor后,kvm_intel模块已经有3个引用了。

3. OpenGL ES:

如果有OpenGL ES的支持,则可以在Emulator中有更好的图像处理能力,比如:可以运行一些3D的程序。

当本机缺少或ADT没找到OpenGL相关的库时,会遇到如下的错误。

 代码如下 复制代码
[2013-10-10 18:34:19 - App2] Launching a new emulator with Virtual Device 'Android-4.2-x86'
[2013-10-10 18:34:26 - Emulator] Failed to load libGL.so
[2013-10-10 18:34:26 - Emulator] error libGL.so: cannot open shared object file: No such file or directory
[2013-10-10 18:34:26 - Emulator] Failed to load libGL.so
[2013-10-10 18:34:26 - Emulator] error libGL.so: cannot open shared object file: No such file or directory
[2013-10-10 18:34:26 - Emulator] emulator: emulator window was out of view and was recentered

解决方法也很简单,一般来说都是有了库的,只是需要添加一个软连接而已;有时,库也缺少,那么就需要先安装GL相关的软件库,再建立软连接。

 代码如下 复制代码
# 如果在/usr/lib下找不到libGL相关的东西,可以用 sudo apt-get install libgl1-mesa-glx  来安装相应的软件库。
jay@jay-linux:~$ ll /usr/lib/x86_64-linux-gnu/mesa/
total 428
drwxr-xr-x  2 root root   4096 Sep  3 17:56 ./
drwxr-xr-x 54 root root  36864 Oct 10 15:07 ../
-rw-r--r--  1 root root     31 Jun 19 04:54 ld.so.conf
lrwxrwxrwx  1 root root     12 Jun 19 04:54 libGL.so.1 -> libGL.so.1.2
-rw-r--r--  1 root root 390352 Jun 19 04:55 libGL.so.1.2
# 可以看到刚好没有libGL.so,就在 /usr/lib/x86_64-linux-gnu/mesa/ 目录下,建立一个指向libGL.so.1.2的软连接libGL.so即可
# 如果没有root权限,则可以在SDK的lib目录中建立软连接亦可,如下所示:
jay@jay-linux:~$ ln -s /usr/lib/x86_64-linux-gnu/mesa/libGL.so.1.2 /home/jay/adt-bundle-linux-x86_64-20130917/sdk/tools/lib/libGL.so

在eclipse中并不能直接开发android程序,需要一个ADT插件,但在64位Ubuntu上,Eclipse中使用ADT时,提示R找不到“R cannot be resolved to a variable”错误,如何解决呢?

在64位Ubuntu上,Eclipse中使用ADT查看Andorid App 代码时,发现很多都时红的(错误),提示为R找不到“R cannot be resolved to a variable”。

我们都知道R.java时自动生成的,通过R可以引用App中的resource。

同时,仔细一看,在gen/my.package/下并没有自动生成R.java文件。

其原因,应该是自动生成R.java的工具的运行需要32bit的一些库,而在64bit系统上默认可能缺少这些库。

解决方案也很简单,直接安装所需的库即可,例如在Ubuntu上:

 代码如下 复制代码
jay@jay-linux:~$ sudo apt-get install ia32-libs



安装好32bit的库后,刷新项目或重启Eclipse后,就正常了。

Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。

周末试玩了一下在Eclipse中使用ADT开发Android应用程序的环境,当然诚如大家都懂的那样,Android Emulator模拟器启动和运行的速度确实是比较慢的,也都“有口皆碑”了的,呵呵。当然,Intel去年开发并在Google Android官方网站发布了一个对Android Emulator的驱动,大幅提升了在Intel x86平台上Android Emulator的启动和运行效率,从而提升Android应用程序开发者在使用Android模拟器开发程序时的效率。
在Windows、MacOS上使用HAXM (Hardware Accelerated Execution Manager) 来加速Android Emulator,在Linux平台上直接使用KVM加速即可。据官方介绍,一般来说使用了HAXM或KVM,有5~10x的性能提高,我简单看了下确实也有几倍的提高。
HAXM必须要求有Intel硬件虚拟化的支持,一般在BIOS中可以设置。BTW,ThinkPad X230的BIOS中,居然是在Security选项中设置VT的。
可以通过Android SDK Manager那下载HAXM, 在 Package 列表的最下面就是要用到的 Intel HAXM 扩展 (Intel x86 Emulator Accelerator (HAXM)),也需要安装对应的Image(Intel x86 Atom System Image)。下载后的HAXM安装文件在sdkextrasintelHardware_Accelerated_Execution_Manager 目录下,点击安装即可(注意如果Intel VT没有打开是不能安装成功的)。

在制作AVD时,需要设置其“CPU/ABI”属性为“Intel Atom (x86)”。对于图形方面的加速,编辑AVD设置时,Emulation Option选择“Use Host GPU”。

Android Virtual Device Manager中启动某个某个AVD时,可能会看到如下:

Starting emulator for AVD 'AVD_for_Nexus_S_by_Google'
emulator: device fd:620
HAX is working and emulator runs in fast virt mode
creating window 0 0 438 729

Eclipse中ADT运行时,对应的输出如下(可以看到HAX是否work):

[2013-07-08 11:44:43 - JayFirstApp] ------------------------------
[2013-07-08 11:44:43 - JayFirstApp] Android Launch!
[2013-07-08 11:44:43 - JayFirstApp] adb is running normally.
[2013-07-08 11:44:43 - JayFirstApp] Performing com.example.jayfirstapp.MainActivity activity launch
[2013-07-08 11:44:43 - JayFirstApp] Automatic Target Mode: launching new emulator with compatible AVD 'AVD_for_Nexus_S_by_Google'
[2013-07-08 11:44:43 - JayFirstApp] Launching a new emulator with Virtual Device 'AVD_for_Nexus_S_by_Google'
[2013-07-08 11:44:47 - Emulator] emulator: device fd:620
[2013-07-08 11:44:47 - Emulator]
[2013-07-08 11:44:47 - Emulator] HAX is working and emulator runs in fast virt mode
[2013-07-08 11:44:50 - Emulator] creating window 0 0 438 729
[2013-07-08 11:44:50 - JayFirstApp] New emulator found: emulator-5554
[2013-07-08 11:44:50 - JayFirstApp] Waiting for HOME ('android.process.acore') to be launched...

BTW,HAXM是我们Team开发的,如果大家使用时遇到什么bug,也可以反馈给我,我可转交给HAXM的developer。 (Update:本人不在那个team,请大家有问题到Intel HAXM官方网站去反馈吧: http://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager)

使用 Intel HAXM 为 Android 模拟器加速,媲美真机

Intel HAXM (Hardware Accelerated Execution Manager) 使用基于 Intel(R) Virtualization Technology (VT) 的硬件加速, 因此需要 CPU 支持 VT , 而且仅限于 Intel CPU, 与 AMD CPU 无缘, Intel HAXM 的描述如下:

使用 Intel VT 技术;

为 Android x86 虚拟设备的模拟运行提供硬件加速;

与 Android SDK 集成;

硬件需求如下:

支持 VT-x, EM64T 以及 Execute Disable Bit 的 Intel 处理器;

至少 1GB 可用内存

支持的操作系统:

Windows 7 (32/64-bit)

Windows Vista (32/64-bit)

Windows XP (32-bit only)

OS X 10.6 or 10.7 (32/64-bit)

下载并安装 Intel HAXM 扩展

启动 Android SDK Manager, 在 Package 列表的最下面就是要用到的 Intel HAXM 扩展, 打勾, 下载, 不用去 Intel 的网站, 如下图:

 

下载并安装 Intel HAXM 扩展

 

下载 HAXM 之后, 需要运行安装程序来进行安装, HAXM 下载的目录是 android-sdk\extras\intel\Hardware_Accelerated_Execution_Manager , 运行 IntelHaxm.exe 进行安装, 根据屏幕提示,一步一步安装即可。

下载 Android x86 镜像

Android SDK Manager 中已经有了 4.1.2 的 x86 镜像, 因此选择 4.1.2 x86 镜像, 如下图所示:

 

下载 Android x86 镜像

 

使用 Android x86 镜像

新建或者编辑 Android 模拟器, 将模拟器 CPU/ABI 设置为 Intel Atom X86 , 如下图所示:

 

配置 Android 模拟器使用 x86 系统镜像

 

如果上面的步骤都没有出错, 现在, Android 模拟器运行的速度几乎可以媲美真机了, 再也不用羡慕 MAC 平台上的 iOS 模拟器。

张志敏所有文章遵循创作共用版权协议,要求署名、非商业 、保持一致。在满足创作共用版权协议的基础上可以转载,但请以超链接形式注明出处。

[!--infotagslink--]

相关文章

  • 使用PHP+JavaScript将HTML页面转换为图片的实例分享

    这篇文章主要介绍了使用PHP+JavaScript将HTML元素转换为图片的实例分享,文后结果的截图只能体现出替换的字体,也不能说将静态页面转为图片可以加快加载,只是这种做法比较interesting XD需要的朋友可以参考下...2016-04-19
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • Python 图片转数组,二进制互转操作

    这篇文章主要介绍了Python 图片转数组,二进制互转操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-09
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • Photoshop古装美女图片转为工笔画效果制作教程

    今天小编在这里就来给各位Photoshop的这一款软件的使用者们来说说把古装美女图片转为细腻的工笔画效果的制作教程,各位想知道方法的使用者们,那么下面就快来跟着小编一...2016-09-14
  • php抓取网站图片并保存的实现方法

    php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
  • jquery左右滚动焦点图banner图片鼠标经过显示上下页按钮

    jquery左右滚动焦点图banner图片鼠标经过显示上下页按钮...2013-10-13
  • Android开发中findViewById()函数用法与简化

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

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神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
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • python opencv通过4坐标剪裁图片

    图片剪裁是常用的方法,那么如何通过4坐标剪裁图片,本文就详细的来介绍一下,感兴趣的小伙伴们可以参考一下...2021-06-04
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • 使用PHP下载CSS文件中的图片的代码

    共享一段使用PHP下载CSS文件中的图片的代码 复制代码 代码如下: <?php //note 设置PHP超时时间 set_time_limit(0); //note 取得样式文件内容 $styleFileContent = file_get_contents('images/style.css'); //not...2013-10-04
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • PHP swfupload图片上传的实例代码

    PHP代码如下:复制代码 代码如下:if (isset($_FILES["Filedata"]) || !is_uploaded_file($_FILES["Filedata"]["tmp_name"]) || $_FILES["Filedata"]["error"] != 0) { $upload_file = $_FILES['Filedata']; $fil...2013-10-04