Android开发者文档笔记(三)

**在APP中使用图片和动画


*如何有效地加载容量大的位图


图片有各种大小和形状,在很多情况下它们的大小超过了所要求的平台的用户界面的分辨率。例如,系统应用程序显示图片库通常使用Android设备的相机分辨率远高于你的设备的屏幕密度。鉴于手机的运行内存有限,理想情况下我们只需要加载一个适应UI组件大小的低分辨率版本的图片到于内存中。




在各种资源中,Bitmapfactory类提供了许多加工方法(如加工字节数组,文件,资源等等)用来生成位图Bitmap。这些方法很容就把内存分配出去,从而导致内存泄漏异常。为此,在生成位图之前,我们必须查看你所要生成的位图大小。


BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;


在知道了大小之后,我们就可以决定以什么样的方式载入图片,通过在Bitmapfactory.Options类中设置inSampleSize为true来告诉编译器,我们所要进行的改变。例如,一个分辨率为2048x1536的图像变成inSampleSize=4的分辨率为512x384的图像,此时图片所占用的内存就是0.75MB,而不是12MB的完整图像(ARGB_8888的图片配置)。有一个方法可以计算样本大小值inSampleSzie=2的幂次方的目标的高度和宽度:

//计算缩小的比例值
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;
        }
    }


    return inSampleSize;
}





//重绘缩小后的位图
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {


    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);


    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);


    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}


//在UI组件中添加图像
mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));




这就是降分辨率的整个过程,计算完整图像的大小,再算出降到目标分辨率所需要的参数值,通过调整图像的边界,重新生成Bitmap对象





*在AsyncTask中处理图像




对于从外部内存获取或者从网络中下载下来的图像的分辨率的降低处理不应该放在UI线程中进行,由于有网速,图片大小以及处理能力的差异,降分辨率处理的时间机油可能变得很长,若在UI线程中进行就会导致线程阻塞而使app崩溃。

//下载
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private int data = 0;


    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference<ImageView>(imageView);
    }


    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        data = params[0];
        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
    }


    // Once complete, see if ImageView is still around and set bitmap.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}


//启动
public void loadBitmap(int resId, ImageView imageView) {
    BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    task.execute(resId);
}



在上述代码中,使用的是AsyncTask这个方法,并且添加了垃圾回收机制用来回收不用的图像资源。当然,如果加载量多的话,可以使用一些第三方的框架,如Volley。




















*并发处理:
例子下载


在例子中使用了WeakReference这个方法,弱引用的概念是:在Java中一个对象要被回收,需要有两个条件:1、没有任何引用指向它。2、能被Java中的垃圾回收机制运行。
而简单的对象在被使用完后,是可以被直接回收,但是cache中缓存的数据是不会被GC运行的,所以也就不会被回收,需要手动清除,所以Java中引入了Weak Reference这个概念,
当一个对象仅仅被Weak Reference所指向时,必然可以被GC运行和回收


弱引用对象:WeakReference<BitmapWorkerTask> weakReferenceTask = new WeakReference(BitmapWorkerTask)(bitmapWorkerTask);
强引用:BitmapWorkerTask task = newBitmapWorkerTask();
判断对象是否被回收:weakReference.get();
如果对象创建后被闲置,就会自动回收


Soft Reference比Weak Reference多一个条件被回收:当运行内存不足时


使用弱引用的三个前提:1、对象有需要被Cache的价值,2、对象不容易被回收。3.对象占内存





*MemoryLruCache缓存图片:



//create a LruCacahe 
private LruCache<String, Bitmap> mMemoryCache;


@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Get max available VM memory, exceeding this amount will throw an
    // OutOfMemory exception. Stored in kilobytes as LruCache takes an
    // int in its constructor.
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);


    // Use 1/8th of the available memory for this memory cache.
    final int cacheSize = maxMemory / 8;


    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // The cache size will be measured in kilobytes rather than
            // number of items.
            return bitmap.getByteCount() / 1024;
        }
    };
    ...
}


public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}


public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}










//loading bitmap
public void loadBitmap(int resId, ImageView imageView) {
    final String imageKey = String.valueOf(resId);


    final Bitmap bitmap = getBitmapFromMemCache(imageKey);
    if (bitmap != null) {
        mImageView.setImageBitmap(bitmap);
    } else {
        mImageView.setImageResource(R.drawable.image_placeholder);
        BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
        task.execute(resId);
    }
}




//how to load bitmap from disk or internet
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final Bitmap bitmap = decodeSampledBitmapFromResource(
                getResources(), params[0], 100, 100));
        addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
        return bitmap;
    }
    ...
}	









*DiskLruCache缓存图片


private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";


@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // Initialize memory cache
    ...
    // Initialize disk cache on background thread
    File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
    new InitDiskCacheTask().execute(cacheDir);
    ...
}


class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
    @Override
    protected Void doInBackground(File... params) {
        synchronized (mDiskCacheLock) {
            File cacheDir = params[0];
            mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
            mDiskCacheStarting = false; // Finished initialization
            mDiskCacheLock.notifyAll(); // Wake any waiting threads
        }
        return null;
    }
}


class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    ...
    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final String imageKey = String.valueOf(params[0]);


        // Check disk cache in background thread
        Bitmap bitmap = getBitmapFromDiskCache(imageKey);


        if (bitmap == null) { // Not found in disk cache
            // Process as normal
            final Bitmap bitmap = decodeSampledBitmapFromResource(
                    getResources(), params[0], 100, 100));
        }


        // Add final bitmap to caches
        addBitmapToCache(imageKey, bitmap);


        return bitmap;
    }
    ...
}


public void addBitmapToCache(String key, Bitmap bitmap) {
    // Add to memory cache as before
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }


    // Also add to disk cache
    synchronized (mDiskCacheLock) {
        if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
            mDiskLruCache.put(key, bitmap);
        }
    }
}


public Bitmap getBitmapFromDiskCache(String key) {
    synchronized (mDiskCacheLock) {
        // Wait while disk cache is started from background thread
        while (mDiskCacheStarting) {
            try {
                mDiskCacheLock.wait();
            } catch (InterruptedException e) {}
        }
        if (mDiskLruCache != null) {
            return mDiskLruCache.get(key);
        }
    }
    return null;
}


// Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getDiskCacheDir(Context context, String uniqueName) {
    // Check if media is mounted or storage is built-in, if so, try and use external cache dir
    // otherwise use internal cache dir
    final String cachePath =
            Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
                    !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
                            context.getCacheDir().getPath();


    return new File(cachePath + File.separator + uniqueName);
}



*其中使用到线程的同步控制,Disk的访问和写入都是不能同时写入或读取的,在这些地方需要多加注意。还有就是对于Disk缓存的读写,必须建立在Disk缓存初始化完成的前提下,因为初始化操作不是放在MainThread中进行的。



*当手机设备的配置改变时,比如转屏后,无论是Activity还是Fragment都需要重新创建。此时,如果你的Activity或者Fragment的UI中有大量数据,如有大量的图片显示,这样重新创建就会消耗更多重复的资源,当然Activity中是自动进行缓存,提供现场恢复。而Fragment就需要我们用Memory Cache进行手动缓存。以往设置Fragment现场恢复的时候,都认为这个方式有点鸡肋,因为并不能打到跟Activity一样的效果,但是,看了文档,才发现,Fragment必须手动申请Memoery Cache缓存才可以。



private LruCache<String, Bitmap> mMemoryCache;


@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    RetainFragment retainFragment =
            RetainFragment.findOrCreateRetainFragment(getFragmentManager());
    mMemoryCache = retainFragment.mRetainedCache;
    if (mMemoryCache == null) {
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            ... // Initialize cache here as usual
        }
        retainFragment.mRetainedCache = mMemoryCache;
    }
    ...
}


class RetainFragment extends Fragment {
    private static final String TAG = "RetainFragment";
    public LruCache<String, Bitmap> mRetainedCache;


    public RetainFragment() {}


    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
        if (fragment == null) {
            fragment = new RetainFragment();
            fm.beginTransaction().add(fragment, TAG).commit();
        }
        return fragment;
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}




*管理位图内存:
看不懂,实力不够,没明白意图


*提示:一般的androdi设备分配给一个app的内存是32mb,而一个Cache比较合理的是使用其内存的八分之一,假如一张为1024*843分辨率的32位真彩照片,那么这张照片的大小就应该为1024*843*4 bytes,而4MB/(1024*843*4 bytes)则是能缓存的张数。


  另外,8位的真彩照片一个像素点是8bit,1字节
16位的真彩照片一个像素点是16bit,2字节
24位的真彩照片一个像素点是24bit,3字节
32位的真彩照片一个像素点是32bit,4字节


黑白二值图像,不压缩的情况下一个像素点是1bit
基于SSM框架的智能家政保洁预约系统,是一个旨在提高家政保洁服务预约效率和管理水平的平台。该系统通过集成现代信息技术,为家政公司、家政服务人员和消费者提供了一个便捷的在线预约和管理系统。 系统的主要功能包括: 1. **用户管理**:允许消费者注册、登录,并管理他们的个人资料和预约历史。 2. **家政人员管理**:家政服务人员可以注册并更新自己的个人信息、服务类别和服务时间。 3. **服务预约**:消费者可以浏览不同的家政服务选项,选择合适的服务人员,并在线预约服务。 4. **订单管理**:系统支持订单的创建、跟踪和管理,包括订单的确认、完成和评价。 5. **评价系统**:消费者可以在家政服务完成后对服务进行评价,帮助提高服务质量和透明度。 6. **后台管理**:管理员可以管理用户、家政人员信息、服务类别、预约订单以及处理用户反馈。 系统采用Java语言开发,使用MySQL数据库进行数据存储,通过B/S架构实现用户与服务的在线交互。系统设计考虑了不同用户角色的需求,包括管理员、家政服务人员和普通用户,每个角色都有相应的权限和功能。此外,系统还采用了软件组件化、精化体系结构、分离逻辑和数据等方法,以便于未来的系统升级和维护。 智能家政保洁预约系统通过提供一个集中的平台,不仅方便了消费者的预约和管理,也为家政服务人员提供了一个展示和推广自己服务的机会。同时,系统的后台管理功能为家政公司提供了强大的数据支持和决策辅助,有助于提高服务质量和管理效率。该系统的设计与实现,标志着家政保洁服务向现代化和网络化的转型,为管理决策和控制提供保障,是行业发展中的重要里程碑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值