Android面试题5

81.双缓存怎么实现的?

答:1、在内存中建立一块“虚拟画布”:
Bitmap bmp = new Bitmap(600, 600);
2、获取这块内存画布的Graphics引用:
Graphics g = Graphics.FromImage(bmp);
3、在这块内存画布上绘图:
g.FillEllipse(brush, i * 10, j * 10, 10, 10);
4、将内存画布画到窗口中
this.CreateGraphics().DrawImage(bmp, 0, 0);

82.垃圾收集算法的核心思想

答:Java语言建立了垃圾收集机制,用以跟踪正在使用的对象和发现并回收不再使用(引用)的对象。该机制可以有效防范动态内存分配中可能发生的两个危险:因内存垃圾过多而引发的内存耗尽,以及不恰当的内存释放所造成的内存非法引用。

  垃圾收集算法的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。垃圾收集算法的选择和垃圾收集系统参数的合理调节直接影响着系统性能,因此需要开发人员做比较深入的了解。

83.触发主GC(Garbage Collector)的条件

答:①当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

②Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

84.减少GC开销的措施

答:

(1)不要显式调用System.gc()

(2)尽量减少临时对象的使用

(3)对象不用时最好显式置为Null

(4)尽量使用StringBuffer,而不用String来累加字符串

(5)能用基本类型如Int,Long,就不用Integer,Long对象

(6)尽量少用静态对象变量

(7)分散对象创建或删除的时间

85.gc与finalize方法

答:finalize(),其工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以如果用finalize()就能在垃圾回收时刻做一些重要的清理工作。

gc 只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存(当使用JNI技术时,可能会在栈上分配内 存,例如java调用c程序,而该c程序使用malloc分配内存时).因此,如果某些对象被分配了栈上的内存区域,那gc就管不着了,对这样的对象进行 内存回收就要靠finalize().

86.ViewPager如何实现?

答:a.Viewpager是一个控件,用的时候直接在布局中定义引用(Android.support.v4.view.ViewPager)即可。  b.ViewPager里面显示的是多个View,用LayoutInflater的对象调用inFlater()方法定义布局,并把View对象添加到List<View>集合中。

    c.定义适配器继承PagerAdapter,设置给ViewPager。

87.请说出使用equal和==比较对象时的区别?

答:“==”比较的是2个对象的地址,而equals比较的是2个对象的内容。例如两个String类型的对象:String s1 = new String(“str”);

String s2 = new String(“str”);

用“==”比较的时候返回的是false,因为创建了两个对象,在内存中的地址不一样,用equals则返回true。

88.什么是Java序列化和反序列话,如何实现Java序列化?

答:1)序列化是将对象状态转化成可保持或传输的格式的过程。与序列化相反的是反序列化,它是将流转化为对象。

2)实现序列化必须要实现Serializable接口,目的是声明此类是可以被序列化的,而且继承此类的子类也可以自动的被序列化。

89.请解释下android程序运行时权限与文件系统权限的区别?

答:apk程序是运行在虚拟机上的,对应的是Android中独有的权限机制,是由Dalvik授权的,而文件系统权限是由linux内核授权的,只有体现到文件系统上时才使用linux的权限设置。

90.讲一讲overload和override的区别,overloaded的方法是否可以改变返回值的类型?

答:overload是方法的重载,Java中的编译时多态,重载的方法具有相同的方法名,但是参数列表不同(参数的类型和数量),对返回值类型没有要求,可以改变;

    override是方法得重写,Java中的运行时多态,为子类在继承父类是重写父类中的方法,方法名,返回值类型和参数列表都相同。

91.如何使用socket实现TCP点对点通信?

答:服务器端监听:首先实例化ServerSocket,并传入端口号(1001-65535),然后用ServerSocket对象调用accept()方法监听,一旦取得连接则获得socket连接对象的客户端。

客户端:实例化Socket,传入服务器的ip和port,一旦连接成功后,Socket对象就可以获得输入、输出流,从而进行通信。当通信完成后,用Socket对象调用close()方法关闭连接,完成一次完整的 socket连接。

92.请简述service可能被kill的场景,kill之后如何自启?

在运行onStartCommand后service进程被kill后,系统将会再次启动service,并传入最后一个intent给onstartCommand。直到调用stopSelf(int)才停止传递intent。如果在被kill后还有未处理好的intent,那被kill后服务还是会自动启动。因此onstartCommand不会接收到任何null的intent

93.请使用递归方式来遍历盘下的所有文件,并计算出所有图片文件的数量?

public class F3 {

public static void main(String[] args) {

File f1=new File("D://");

m1(f1);

}

 static void m1(File f1) {

// TODO Auto-generated method stub

 if(f1.exists()){

File file[]=f1.listFiles();

for(int i=0;i<file.length;i++){

if(file[i].isFile()){

System.out.println(file[i]);

}else{

m1(file[i]);

}

}

}else{

System.out.println("不存在!!!!");

}

}

}

94.现有两个单向链表,我想知道这两个链表的相交情况。(可以不写实现,但思路必须写清)?

答:方法一:直接法

直接判断第一个链表的每个结点是否在第二个链表中,时间复杂度为O(len1*len2),耗时很大

方法二:利用计数

如果两个链表相交,则两个链表就会有共同的结点;而结点地址又是结点唯一标识。因而判断两个链表中是否存在地址一致的节点,就可以知道是否相交了。可以对第一 个链表的节点地址进行hash排序,建立hash表,然后针对第二个链表的每个节点的地址查询hash表,如果它在hash表中出现,则说明两个链表有共 同的结点。这个方法的时间复杂度为:O(max(len1+len2);但同时还得增加O(len1)的存储空间存储哈希表。这样减少了时间复杂度,增加 了存储空间。

以链表节点地址为值,遍历第一个链表,使用Hash保存所有节点地址值,结束条件为到最后一个节点(无环)或Hash中该地址值已经存在(有环)。

再遍历第二个链表,判断节点地址值是否已经存在于上面创建的Hash表中。

这个方面可以解决题目中的所有情况,时间复杂度为O(m+n),m和n分别是两个链表中节点数量。由于节点地址指针就是一个整型,假设链表都是在堆中动态创建的,可以使用堆的起始地址作为偏移量,以地址减去这个偏移量作为Hash函数

方法三

两个没有环的链表相交于一节点,则在这个节点之后的所有结点都是两个链表所共有的。如果它们相交,则最后一个结点一定是共有的,则只需要判断最后一个结点是否相同即可。时间复杂度为O(len1+len2)。对于相交的第一个结点,则可求出两个链表的长度,然后用长的减去短的得到一个差值 K,然后让长的链表先遍历K个结点,然后两个链表再开始比较。还可以这样:其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个

/************************************************************************/
/* 两个不含环的单链表的相交
相交指的是结点的地址相同,而不是结点的值相同 */
/************************************************************************/

 

95.使用javac/c++写一段程序,找出数组中出现次数最多数字,并输出出现次数,请标明算法的时间复杂值?

import java.util.HashMap;  

import java.util.Iterator;  

public class Test   

{

    public static void main(String[] args)   

    {  

        int a [] = {6,6,6,1,2,8,8,5,5,5,12};  

        int maxnum = 0;  

        int maxvalue = 0;  

        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();  

        HashMap<Integer,Integer> map2 = new HashMap<Integer,Integer>();  

        for(int i=0;i<a.length;i++)  

        {  

            int count = 0;  

            for(int j=0;j<a.length;j++)  

            {  

                if(a[i]==a[j])  

                {  

                    count++;  

                }  

            }  

            map.put(a[i], count);//map会把重复的数据过滤掉  

        }  

        Iterator it =  map.keySet().iterator();  

        Iterator it2 = map.keySet().iterator();      

        while(it.hasNext())  

        {  

            Object key = it.next();  

            if(map.get(key)>maxnum)  

            {  

               maxnum = map.get(key);  

                maxvalue = (Integer)key;  

            }  

        }  

        map2.put(maxvalue, maxnum);  

        while(it2.hasNext())  

        {  

            Object key = it2.next();  

            if(map.get(key)==maxnum)  

            {  

                map2.put((Integer)key, maxnum);  

            }  

        }  

        Iterator it3 = map2.keySet().iterator();  

        while(it3.hasNext())  

       {  

            Object key = it3.next();  

            System.out.print("出现次数最多的为:"+key+"  ");  

            System.out.println("出现次数:"+map2.get(key));  

        }  

}}

96.什么是OAuth,Oauth,的角色,Qauth验证流程?

答:<1>OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth是个安全相关的协议,作用在于,使用户授权第三方的应用程序访问用户的web资源,并且不需要向第三方应用程序透露自己的密码。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要分享他们的访问许可或他们数据的所有内容。

OAuth是OpenID的一个补充,但是完全不同的服务

<2>OAuth协议中包含了三个角色:
Service Provdier,即服务提供者,如新浪微博;
User,即普通用户,如新浪微博用户;
Consumer,即第三方应用,如本人开发的应用

<3>我在第三方应用A有需要帐号、密码登录后才能访问的一些数据,我想让用户登录腾讯的AlloyOS就可以访问这些数据,但是我不想把密码告诉腾讯,这时候就可以通过腾讯的QAuth来解决,给AlloyOS一个token,当用户确认绑定账号之后,用户只要登录的AlloyOS,打开第三方应用A时就通过token直接登录的第三方应用。

97.使用javac/c++写一段程序,找出数组中第K大小的数,输出数所在的位置,请标明算法的时间复杂度?

答:

import java.util.ArrayList;

public class Biaoshi{

public static void main(String args[]){

ArrayList<String> al=new ArrayList<String>();

 //向Java动态数组中添加数据

 al.add("a");

 al.add("b");

 al.add("c");

 //输出Java动态数组

 int j=0;

 for(int i=0;i<al.size();i++)

 {

 j++;

  String k=(String)al.get(i);

  System.out.println(k+"--------"+j);

 }

 //删除数组中的某个元素,删除第二个元素

 al.remove(1);

 //修改Java动态数组,把新的元素放到第二个位置

 al.add(1,"2");

 输出Java动态数组

 for(int i=0;i<al.size();i++)

 {

  String k=(String)al.get(i);

  System.out.println(k);

 }

}

}

 

98.进程通信和线程同步的方法?

答:进程通信方法:

  1. 信号(signal)
    2.管道(pipe):父子进程
    3.命名管道:mkfifo
    4.socket
    5.消息队列:msgget/msgctl/msgrcv/msgsnd
    6.共享内存:shmget/shmat/shmdt/shmctl
    7.信号灯:semget/semctl/semop/
    8.锁文件(flock)

9. DDE是一种动态数据交换机制(Dynamic Data Exchange,DDE)。使用DDE通讯需要两个Windows应用程序,其中一个作为服务器处理信息,另外一个作为客户机从服务器获得信息。客户机应用程序向当前所激活的服务器应用程序发送一条消息请求信息,服务器应用程序根据该信息作出应答,从而实现两个程序之间的数据交换。

  1. 线程同步方法:
    1.互斥量Mutex
    2.信号灯Semophore
    3.条件变量Conditions

 

99.面向对象的特征有哪些方面?

面向对象的三个基本特征是:封装、继承、多态。

封装:封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

 

继承:面向对象编程 (OOP) 语言的一个主要功能就是继承。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

通过继承创建的新类称为“子类”或“派生类”。

被继承的类称为基类父类超类

继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过继承Inheritance)和组合Composition)来实现。

在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

多态:多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。

重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用家谱中任一类的实例的某一属性时的正确调用

100.Context里面主要包括什么具体的东西?

答:Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型。同时启动应用级的操作,如启动Activity,broadcasting和接收intents。

101.简述条形码的扫描设计思路?

   生成步骤:

1、获取需要生成图形的数据

2、根据Zxing提供的图形算法进行图片的生成

3、展示给用户看

扫描步骤:

1、开启扫描,通过startActivityforResult

2、初始化surfaceView、Camera、自定义View

3、开启DecodeThread去获取并解析自定义View里的图片,通过CaptureActivityHandler来处理解析结果

4、当解析成功,handler发送回到主线程,执行返回上一个页面将解析出来的值返回,通过setResult方法返回

102. ListView异步加载图片实现思路(优化篇)

异步加载图片基本思想:

1.先从内存缓存中获取图片显示(内存缓冲)

2.获取不到的话从SD卡里获取(SD卡缓冲)

3.都获取不到的话从网络下载图片并保存到SD卡同时加入内存并显示(视情况看是否要显示)

OK,先上adapter的代码:

public class LoaderAdapter extends BaseAdapter{

private static final String TAG = "LoaderAdapter";

private boolean mBusy = false;

public void setFlagBusy(boolean busy) {

this.mBusy = busy;

}

private ImageLoader mImageLoader;

private int mCount;

private Context mContext;

private String[] urlArrays;

public LoaderAdapter(int count, Context context, String []url) {

this.mCount = count;

this.mContext = context;

urlArrays = url;

mImageLoader = new ImageLoader(context);

}

public ImageLoader getImageLoader(){

return mImageLoader;

}

@Override

public int getCount() {

return mCount;

}

@Override

public Object getItem(int position) {

return position;

}

@Override

public long getItemId(int position) {

return position;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder viewHolder = null;

if (convertView == null) {

convertView = LayoutInflater.from(mContext).inflate(

R.layout.list_item, null);

viewHolder = new ViewHolder();

viewHolder.mTextView = (TextView) convertView

.findViewById(R.id.tv_tips);

viewHolder.mImageView = (ImageView) convertView

.findViewById(R.id.iv_image);

convertView.setTag(viewHolder);

} else {

viewHolder = (ViewHolder) convertView.getTag();

}

String url = "";

url = urlArrays[position % urlArrays.length];

viewHolder.mImageView.setImageResource(R.drawable.ic_launcher);

if (!mBusy) {

mImageLoader.DisplayImage(url, viewHolder.mImageView, false);

viewHolder.mTextView.setText("--" + position

+ "--IDLE ||TOUCH_SCROLL");

} else {

mImageLoader.DisplayImage(url, viewHolder.mImageView, true);

viewHolder.mTextView.setText("--" + position + "--FLING");

}

return convertView;

}

static class ViewHolder {

TextView mTextView;

ImageView mImageView;

}

}

 

关键代码是ImageLoader的DisplayImage方法,再看ImageLoader的实现

public class ImageLoader {

private MemoryCache memoryCache = new MemoryCache();

private AbstractFileCache fileCache;

private Map<ImageView, String> imageViews = Collections

.synchronizedMap(new WeakHashMap<ImageView, String>());

// 线程池

private ExecutorService executorService;

public ImageLoader(Context context) {

fileCache = new FileCache(context);

executorService = Executors.newFixedThreadPool(5);

}

// 最主要的方法

public void DisplayImage(String url, ImageView imageView, boolean isLoadOnlyFromCache) {

imageViews.put(imageView, url);

// 先从内存缓存中查找

Bitmap bitmap = memoryCache.get(url);

if (bitmap != null)

imageView.setImageBitmap(bitmap);

else if (!isLoadOnlyFromCache){

// 若没有的话则开启新线程加载图片

queuePhoto(url, imageView);

}

}

private void queuePhoto(String url, ImageView imageView) {

PhotoToLoad p = new PhotoToLoad(url, imageView);

executorService.submit(new PhotosLoader(p));

}

private Bitmap getBitmap(String url) {

File f = fileCache.getFile(url);

// 先从文件缓存中查找是否有

Bitmap b = null;

if (f != null && f.exists()){

b = decodeFile(f);

}

if (b != null){

return b;

}

// 最后从指定的url中下载图片

try {

Bitmap bitmap = null;

URL imageUrl = new URL(url);

HttpURLConnection conn = (HttpURLConnection) imageUrl

.openConnection();

conn.setConnectTimeout(30000);

conn.setReadTimeout(30000);

conn.setInstanceFollowRedirects(true);

InputStream is = conn.getInputStream();

OutputStream os = new FileOutputStream(f);

CopyStream(is, os);

os.close();

bitmap = decodeFile(f);

return bitmap;

} catch (Exception ex) {

Log.e("", "getBitmap catch Exception...\nmessage = " + ex.getMessage());

return null;

}

}

// decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的 

private Bitmap decodeFile(File f) {

try {

// decode image size

BitmapFactory.Options o = new BitmapFactory.Options();

o.inJustDecodeBounds = true;

BitmapFactory.decodeStream(new FileInputStream(f), null, o);

// Find the correct scale value. It should be the power of 2.

final int REQUIRED_SIZE = 100;

int width_tmp = o.outWidth, height_tmp = o.outHeight;

int scale = 1;

while (true) {

if (width_tmp / 2 < REQUIRED_SIZE

|| height_tmp / 2 < REQUIRED_SIZE)

break;

width_tmp /= 2;

height_tmp /= 2;

scale *= 2;

}

// decode with inSampleSize

BitmapFactory.Options o2 = new BitmapFactory.Options();

o2.inSampleSize = scale;

return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);

} catch (FileNotFoundException e) {

}

return null;

}

// Task for the queue

private class PhotoToLoad {

public String url;

public ImageView imageView;

public PhotoToLoad(String u, ImageView i) {

url = u;

imageView = i;

}

}

class PhotosLoader implements Runnable {

PhotoToLoad photoToLoad;

PhotosLoader(PhotoToLoad photoToLoad) {

this.photoToLoad = photoToLoad;

}

@Override

public void run() {

if (imageViewReused(photoToLoad))

return;

Bitmap bmp = getBitmap(photoToLoad.url);

memoryCache.put(photoToLoad.url, bmp);

if (imageViewReused(photoToLoad))

return;

BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);

// 更新的操作放在UI线程中

Activity a = (Activity) photoToLoad.imageView.getContext();

a.runOnUiThread(bd);

}

}

/**

* 防止图片错位

* @param photoToLoad

* @return

*/

boolean imageViewReused(PhotoToLoad photoToLoad) {

String tag = imageViews.get(photoToLoad.imageView);

if (tag == null || !tag.equals(photoToLoad.url))

return true;

return false;

}

// 用于在UI线程中更新界面

class BitmapDisplayer implements Runnable {

Bitmap bitmap;

PhotoToLoad photoToLoad;

public BitmapDisplayer(Bitmap b, PhotoToLoad p) {

bitmap = b;

photoToLoad = p;

}

public void run() {

if (imageViewReused(photoToLoad))

return;

if (bitmap != null)

photoToLoad.imageView.setImageBitmap(bitmap);

}

}

public void clearCache() {

memoryCache.clear();

fileCache.clear();

}

public static void CopyStream(InputStream is, OutputStream os) {

final int buffer_size = 1024;

try {

byte[] bytes = new byte[buffer_size];

for (;;) {

int count = is.read(bytes, 0, buffer_size);

I f (count == -1)

break;

os.write(bytes, 0, count);

}

} catch (Exception ex) {

Log.e("", "CopyStream catch Exception...");

}

}

}

先从内存中加载,没有则开启线程从SD卡或网络中获取,这里注意从SD卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅,这是优化一。于此同时,在adapter里有个busy变量,表示listview是否处于滑动状态,如果是滑动状态则仅从内存中获取图片,没有的话无需再开启线程去外存或网络获取图片,这是优化二。ImageLoader里的线程使用了线程池,从而避免了过多线程频繁创建和销毁,有的童鞋每次总是new一个线程去执行这是非常不可取的,好一点的用的AsyncTask类,其实内部也是用到了线程池。在从网络获取图片时,先是将其保存到sd卡,然后再加载到内存,这么做的好处是在加载到内存时可以做个压缩处理,以减少图片所占内存,这是优化三。

 

而图片错位问题的本质源于我们的listview使用了缓存convertView,假设一种场景,一个listview一屏显示九个item,那么在拉出第十个item的时候,事实上该item是重复使用了第一个item,也就是说在第一个item从网络中下载图片并最终要显示的时候其实该item已经不在当前显示区域内了,此时显示的后果将是在可能在第十个item上输出图像,这就导致了图片错位的问题。所以解决之道在于可见则显示,不可见则不显示。在ImageLoader里有个imageViews的map对象,就是用于保存当前显示区域图像对应的url集,在显示前判断处理一下即可。

下面再说下内存缓冲机制,本例采用的是LRU算法,先看看MemoryCache的实现

复制代码代码如下:

public class MemoryCache {

private static final String TAG = "MemoryCache";

// 放入缓存时是个同步操作

// LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU

// 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率

private Map<String, Bitmap> cache = Collections

.synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true));

// 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存

private long size = 0;// current allocated size

// 缓存只能占用的最大堆内存

private long limit = 1000000;// max memory in bytes

public MemoryCache() {

// use 25% of available heap size

setLimit(Runtime.getRuntime().maxMemory() / 10);

}

public void setLimit(long new_limit) {

limit = new_limit;

Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");

}

public Bitmap get(String id) {

try {

if (!cache.containsKey(id))

return null;

return cache.get(id);

} catch (NullPointerException ex) {

return null;

}

}

public void put(String id, Bitmap bitmap) {

try {

if (cache.containsKey(id))

size -= getSizeInBytes(cache.get(id));

cache.put(id, bitmap);

size += getSizeInBytes(bitmap);

checkSize();

} catch (Throwable th) {

th.printStackTrace();

}

}

/** 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存 */

private void checkSize() {

Log.i(TAG, "cache size=" + size + " length=" + cache.size());

if (size > limit) {

// 先遍历最近最少使用的元素

Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();

while (iter.hasNext()) {

Entry<String, Bitmap> entry = iter.next();

size -= getSizeInBytes(entry.getValue());

iter.remove();

if (size <= limit)

break;

}

Log.i(TAG, "Clean cache. New size " + cache.size());

}

}

public void clear() {

cache.clear();

}

/**

* 图片占用的内存 */

long getSizeInBytes(Bitmap bitmap) {

if (bitmap == null)

return 0;

return bitmap.getRowBytes() * bitmap.getHeight();

}

}

首先限制内存图片缓冲的堆内存大小,每次有图片往缓存里加时判断是否超过限制大小,超过的话就从中取出最少使用的图片并将其移除,当然这里如果不采用这种方式,换做软引用也是可行的,二者目的皆是最大程度的利用已存在于内存中的图片缓存,避免重复制造垃圾增加GC负担,OOM溢出往往皆因内存瞬时大量增加而垃圾回收不及时造成的。只不过二者区别在于LinkedHashMap里的图片缓存在没有移除出去之前是不会被GC回收的,而SoftReference里的图片缓存在没有其他引用保存时随时都会被GC回收。所以在使用LinkedHashMap这种LRU算法缓存更有利于图片的有效命中,当然二者配合使用的话效果更佳,即从LinkedHashMap里移除出的缓存放到SoftReference里,这就是内存的二级缓存,

 

103. android下大文件分割上传 

1.由于android自身的原因,对大文件(如影视频文件)的操作很容易造成OOM,即:Dalvik堆内存溢出,利用文件分割将大文件分割为小文件可以解决问题。

 

•文件分割后分多次请求服务。

  //文件分割上传    

 public  void cutFileUpload(String fileType,String filePath){

         try{

              FileAccessI fileAccessI = new FileAccessI(filePath, 0);

              Long nStartPos = 0l;

              Long length = fileAccessI.getFileLength();

              int mBufferSize = 1024 * 100; //每次处理1024 * 100字节

            byte[] buffer = new byte[mBufferSize];

            FileAccessI.Detail detail;

             long nRead = 0l;

           String vedioFileName = Usual.f_getUUID(); //分配一个文件名

           long nStart = nStartPos;

             int i = 0;

             while (nStart < length)

            {

                detail = fileAccessI.getContent(nStart);

                 nRead = detail.length;

                 buffer = detail.b;

                 JSONObject mInDataJson = new JSONObject();

                 mInDataJson.put("a", "282");

                mInDataJson.put("FileName", vedioFileName);

                mInDataJson.put("start", nStart); //服务端获取开始文章进行写文件

                 mInDataJson.put("filetype", fileType);

                 nStart += nRead;

                 nStartPos = nStart;

               String url = UsualA.f_getXmlSOAUrl(UsualA.mServiceFastByteUrl, "n.uploadvedio", mInDataJson.toString(),

                        "282");

                 HttpFastUtil.f_httpPostByte(url, buffer, false);

             }

         }

        catch (Exception e)

        {         }

•文件分割类

import java.io.*;

 public class FileAccessI implements Serializable {

     RandomAccessFile oSavedFile;

      long nPos;

  public FileAccessI() throws IOException

     {

         this("", 0);

     }

     public FileAccessI(String sName, long nPos) throws IOException

     {

         oSavedFile = new RandomAccessFile(sName, "rw");//创建一个随机访问文件类,可读写模式

         this.nPos = nPos;

         oSavedFile.seek(nPos);

     }

     public synchronized int write(byte[] b, int nStart, int nLen)

     {

         int n = -1;

         try

         {

             oSavedFile.write(b, nStart, nLen);

             n = nLen;

         }

         catch (IOException e)

         {

            e.printStackTrace();

         }

         return n;

     }

     //每次读取102400字节

     public synchronized Detail getContent(long nStart)

     {

         Detail detail = new Detail();

         detail.b = new byte[102400];

         try

         {

            oSavedFile.seek(nStart);

            detail.length = oSavedFile.read(detail.b);

         }

         catch (IOException e)

         {

             e.printStackTrace();

        }

         return detail;

     }

     public class Detail

     {

   public byte[] b;

   public int length;

     }

  //获取文件长度

     public long getFileLength()

     {

         Long length = 0l;

         try

        {

             length = oSavedFile.length();

         }

         catch (IOException e)

         {

             // TODO Auto-generated catch block

             e.printStackTrace();

         }

         return length;

     }

 }

 

•服务端获得分割的文件,利用RandomAccessFile进行文件整理  

/**

       * 音视频图片处理

       * @param mStr

       * @return

       * @throws Exception

       */

      public static String f_uploadVedio(String mStr) throws Exception

      {

          String mResult = Usual.mEmpty;

         String fileType = Usual.mEmpty;

         int startPosL = 0;

         RandomAccessFile oSavedFile = null;

         JSONObject jsonObject = new JSONObject(mStr);

         String vedioJsonStr = jsonObject.getString("VEDIO");

         byte[] vedioBytes = Usual.f_fromBase64String(vedioJsonStr);

         startPosL = (Integer) jsonObject.get("start"); //接收客户端的开始位置(文件读取到的  字节大小)

         fileType = (String)jsonObject.getString("filetype");

         String fileName = (String)jsonObject.getString("FileName");

         if(fileType.equals("picture"))

         {

             oSavedFile = new RandomAccessFile("E:\\"+fileName+".jpg","rw");

         }

         else if(fileType.equals("photo"))

         {

            oSavedFile = new RandomAccessFile("E:\\"+fileName+".jpg","rw");

         }

         else if(fileType.equals("voice"))

          {

             oSavedFile = new RandomAccessFile("E:\\"+fileName+".mp3","rw");

         }

         else if(fileType.equals("video"))

         {

             oSavedFile = new RandomAccessFile("E:\\"+fileName+".mp4", "rw");

         }

         //设置标志位,标志文件存储的位置

         oSavedFile.seek(startPosL);

         oSavedFile.write(vedioBytes);

         oSavedFile.close();

         mResult = "000";

         return mResult;

     }

104.  android listview 异步加载图片并防止错位

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作.如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题。

我简单分析一下:

当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView.当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Item8 复用的是Item1 的 view 如果没有异步不会有任何问题,虽然 Item8 和 Item1 指向的是同一个 view,但滑到Item8 时刷上了 Item8 的数据,这时 Item1 的数据和 Item8 是一样的,因为它们指向的是同一块内存,但 Item1 已滚出了屏幕你看不见。当 Item1 再次可见时这块 view 又刷上了 Item8 的数据。

但当有异步下载时就有问题了,假设 Item1 的图片下载的比较慢,Item8 的图片下载的比较快,你滚上去使 Item8 可见,这时 Item8 先显示它自己下载的图片没错,但等到 Item1 的图片也下载完时你发现Item8 的图片也变成了 Item1 的图片,因为它们复用的是同一个 view。 如果 Item1 的图片下载的比Item8 的图片快, Item1 先刷上自己下载的图片,这时你滑下去,Item8 的图片还没下载完, Item8会先显示 Item1 的图片,因为它们是同一快内存,当 Item8 自己的图片下载完后 Item8 的图片又刷成了自己的,你再滑上去使 Item1 可见, Item1 的图片也会和 Item8 的图片是一样的,

因为它们指向的是同一块内存。

最简单的解决方法就是网上说的,给 ImageView 设置一个 tag, 并预设一个图片

当 Item1 比 Item8 图片下载的快时, 你滚下去使 Item8 可见,这时 ImageView 的 tag 被设成了

Item8 的 URL, 当 Item1 下载完时,由于 Item1 不可见现在的 tag 是 Item8 的 URL,所以不满足条件,虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL。

关键代码如下: 

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

    if (convertView == null) {
        holder = new ViewHolder();
        convertView = LayoutInflater.from(context).inflate(
                R.layout.list_item, null);
        holder.img = (ImageView) convertView.findViewById(R.id.userimage);

        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    User user = list.get(position);

    // 给 ImageView 设置一个 tag
    holder.img.setTag(user.getImgUrl());
    // 预设一个图片
    holder.img.setImageResource(R.drawable.ic_launcher);

    final String tmpImageUrl = user.getImgUrl();

    if (user.getImgUrl() != null && !user.getImgUrl().equals("")) {
        Bitmap bitmap = imageLoader.loadImage(holder.img, user.getImgUrl(),
                new ImageDownloadCallBack() {

                    @Override
                    public void onImageDownloaded(ImageView imageView, Bitmap bitmap) {
                        // 通过 tag 来防止图片错位
                        if (imageView.getTag() != null && 
			imageView.getTag().equals(tmpImageUrl)) { //非user.getImgUrl()
                            imageView.setImageBitmap(bitmap);
                        }
                    }
                });

        if (bitmap != null) {
            holder.img.setImageBitmap(bitmap);
        }
    }

    return convertView;
}

我参考网上资料写了一个 listview 异步加载图片的 DEMO:

(1) 使用线程池

 没有线程池,当图片非常多,快速滑动  listview 时由于下载每个图片都开了一个线程,

 可能出现 OOM (out of memory)。

(2) 内存、文件双缓存

 这里也使用 SoftReference 软引用  

**
 * 图片异步加载类
 * 
 * @author Leslie.Fang
 * @company EnwaySoft
 */
public class AsyncImageLoader {
    // 最大线程数
    private static final int MAX_THREAD_NUM = 10;
    private Map<String, SoftReference<Bitmap>> imageCaches = null;
    private FileUtil fileUtil;
   
 private ExecutorService threadPools = null; // 线程池

    public AsyncImageLoader(Context context) {
        imageCaches = new HashMap<String, SoftReference<Bitmap>>();
        fileUtil = new FileUtil(context);
    }

    public Bitmap loadImage(final ImageView imageView, final String imageUrl,
            final ImageDownloadCallBack imageDownloadCallBack) {
        final String filename = imageUrl
                .substring(imageUrl.lastIndexOf("/") + 1);
        final String filepath = fileUtil.getAbsolutePath() + "/" + filename;

        // 先从软引用中找
        if (imageCaches.containsKey(imageUrl)) {
            SoftReference<Bitmap> reference = imageCaches.get(imageUrl);
            Bitmap bitmap = reference.get();

            // 软引用中的 Bitmap 对象可能随时被回收
            // 如果软引用中的 Bitmap 已被回收,则从文件中找
            if (bitmap != null) {
                Log.i("aaaa", "cache exists " + filename);

                return bitmap;
            }
        }

        // 从文件中找
        if (fileUtil.isBitmapExists(filename)) {
            Log.i("aaaa", "file exists " + filename);
            Bitmap bitmap = BitmapFactory.decodeFile(filepath);

            // 重新加入到内存软引用中
            imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));

            return bitmap;
        }

        // 软引用和文件中都没有再从网络下载
        if (imageUrl != null && !imageUrl.equals("")) {
            if (threadPools == null) {
                threadPools = Executors.newFixedThreadPool(MAX_THREAD_NUM);
            }

            final Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 111 && imageDownloadCallBack != null) {
                        Bitmap bitmap = (Bitmap) msg.obj;
                        imageDownloadCallBack.onImageDownloaded(imageView,
                                bitmap);
                    }
                }
            };

            Thread thread = new Thread() {
                @Override
                public void run() {
                    Log.i("aaaa", Thread.currentThread().getName()
                            + " is running");
                    InputStream inputStream = HTTPService.getInstance()
                            .getStream(imageUrl);
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                    // 图片下载成功重新缓存并执行回调刷新界面
                    if (bitmap != null) {
                        // 加入到软引用中
                        imageCaches.put(imageUrl, new SoftReference<Bitmap>(
                                bitmap));
                        // 缓存到文件系统
                        fileUtil.saveBitmap(filepath, bitmap);

                        Message msg = new Message();
                        msg.what = 111;
                        msg.obj = bitmap;
                        handler.sendMessage(msg);
                    }
                }
            };

            threadPools.execute(thread);
        }

        return null;
    }

    public void shutDownThreadPool() {
        if (threadPools != null) {
            threadPools.shutdown();
            threadPools = null;
        }
    }

    /**
     * 图片下载完成回调接口
     * 
     */
    public interface ImageDownloadCallBack {
        void onImageDownloaded(ImageView imageView, Bitmap bitmap);
    }
}

五篇博客,104道Android面试题,希望可以帮到大家。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值