Android面试题
Posted lxn_李小牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android面试题相关的知识,希望对你有一定的参考价值。
1.Activity启动模式
如何查看当前的Activity栈以及Activity栈中的Activity信息
adb shell dumpsys activity activities
结果如下
一般会有两个Stack,Stack#0代表Launcher所在的Activity
另外的一个Stack就是我们自己的Activity
Task代表一个任务栈,如果有多个任务栈,会有多个Task
Hist代表任务栈中某个Activity,如果当前任务栈中有多个Activity,则会有多个Hist #
需要说明的是,通过在清单文件中给Activity指定启动模式为singleTask和启动时添加标记FLAG_ACTIVITY_NEW_TASK效果是一样的,在启动目标Activity的时候,首先在系统中查找当前的Task中有没有和目标Activity的taskAffinity相同的,有的话就在当前Task中启动,没有的话就新建一个Task
2.动态代理
下面我们用代码来解释动态代理
public interface Caculator //统一接口,代理类和真实对象需要实现此接口
int add(int a, int b);
/**
* 真实对象
*/
public class CaculatorImpl implements Caculator
@Override
public int add(int a, int b)
System.out.println("==============add");
return a + b;
/**
* 代理类,需要实现InvocationHandler接口
*/
class MyInvocationHandler implements InvocationHandler
private Caculator mTarget;
//绑定被代理类,并且返回代理对象
Object bind(Caculator obj)
mTarget = obj;
return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(),mTarget.getClass().getInterfaces(),this);
/**
*
* @param o 被代理的真实对象
* @param method 被代理对象的方法
* @param objects 方法参数
* @return 方法返回值
*/
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable
System.out.println("============before");
//调用被代理对象的方法
Object result = method.invoke(mTarget, objects);
System.out.println("============after" + result);
return result;
public class ProxyTest //测试方法
public static void main(String[] args)
Caculator caculator = new CaculatorImpl();//创建被代理对象
Caculator proxy = (Caculator) new MyInvocationHandler().bind(caculator);//创建代理对象,
proxy.add(2,3);
动态代理应用场景
日志集中打印
事物
AOP
权限管理
下面看下生成代理类的流程
我们从源码的角度来看看整个过程
private static final Class<?>[] constructorParams = InvocationHandler.class ;@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException Objects.requireNonNull(h);//检查我们传入的InvocationHandler是否为null final Class<?>[] intfs = interfaces.clone(); /* * 创建代理类的字节码对象 */ Class<?> cl = getProxyClass0(loader, intfs); /* */ try //通过反射获取代理对象的构造函数,这个构造函数的参数为InvocationHandler类型 final Constructor<?> cons = cl.getConstructor(constructorParams); if (!Modifier.isPublic(cl.getModifiers())) AccessController.doPrivileged(new PrivilegedAction<Void>() public Void run() cons.setAccessible(true); return null; ); return cons.newInstance(new Object[]h);//通过构造函数创建对象,需要InvocationHandler类型的参数 catch (IllegalAccessException|InstantiationException e) throw new InternalError(e.toString(), e); catch (InvocationTargetException e) Throwable t = e.getCause(); if (t instanceof RuntimeException) throw (RuntimeException) t; else throw new InternalError(t.toString(), t); catch (NoSuchMethodException e) throw new InternalError(e.toString(), e); 我们看看getProxyClass0方法是如何创建代理对象的
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces)
if (interfaces.length > 65535)
throw new IllegalArgumentException("interface limit exceeded");
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
获取代理类的步骤可以总结为一下的流程:
- 基于代理接口查找ClassLoader中是否有代理对象的类,如果有,从缓存中取一个
- 如果没有,利用ProxyClassFactory生成一个proxy字节码,具体过程是在ProxyClassFactory的apply方法中
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
// 代理类名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces)
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces)
/*
* 判断接口对classloader是否可见
*/
Class<?> interfaceClass = null;
try
interfaceClass = Class.forName(intf.getName(), false, loader);
catch (ClassNotFoundException e)
if (interfaceClass != intf)
throw new IllegalArgumentException(
intf + " is not visible from class loader");
/*
* 判断class是否为一个接口
*/
if (!interfaceClass.isInterface())
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
/*
* 判断接口是否重复
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null)
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces)
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags))
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null)
proxyPkg = pkg;
else if (!pkg.equals(proxyPkg))
throw new IllegalArgumentException(
"non-public interfaces from different packages");
if (proxyPkg == null)
//包名
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
/*
* 代理类的名称
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 通过ProxyGenerator创建代理类
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
catch (ClassFormatError e)
throw new IllegalArgumentException(e.toString());
我们可以用下面的代码将JDK为我们生成的字节码生成在磁盘上
private static void generateProxy()
FileOutputStream fos = null;
//获取代理类的字节码
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy", new Class[]Caculator.class);
try
fos = new FileOutputStream("$Proxy0.class");
fos.write(bytes);
catch (Exception e)
e.printStackTrace();
finally
try
if (fos != null)
fos.close();
catch (IOException e)
e.printStackTrace();
生成的字节码对象的代码
public final class $Proxy0 extends Proxy implements UserService
private static Method m1;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
//构造方法,通过烦着调用创建对象,参数InvocationHandler是通过Proxy.newProxyInstance传过来的
public $Proxy0(InvocationHandler var1) throws
super(var1);//把我们的InvocationHandler对象传递给父类Proxy的h成员变量
public final boolean equals(Object var1) throws
try
return ((Boolean)super.h.invoke(this, m1, new Object[]var1)).booleanValue();
catch (RuntimeException | Error var3)
throw var3;
catch (Throwable var4)
throw new UndeclaredThrowableException(var4);
public final String toString() throws
try
return (String)super.h.invoke(this, m2, (Object[])null);
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
public final boolean getValue() throws
try
return ((Boolean)super.h.invoke(this, m4, (Object[])null)).booleanValue();
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
public final int hashCode() throws
try
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
catch (RuntimeException | Error var2)
throw var2;
catch (Throwable var3)
throw new UndeclaredThrowableException(var3);
public final void getName(String var1) throws
try
super.h.invoke(this, m3, new Object[]var1);
catch (RuntimeException | Error var3)
throw var3;
catch (Throwable var4)
throw new UndeclaredThrowableException(var4);
//通过反射获取方法,传给invoke方法
static
try
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("UserService").getMethod("getValue");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("UserService").getMethod("getName", Class.forName("java.lang.String"));
catch (NoSuchMethodException var2)
throw new NoSuchMethodError(var2.getMessage());
catch (ClassNotFoundException var3)
throw new NoClassDefFoundError(var3.getMessage());
3.加载大图片
android3.0之前,bitmap的像素数据存放在Native内存,native内存的释放是不确定的,容易出现溢出,所以不用的时候需要
调用recycle()方法回收bitmap占用的内存。
BitmapFactory.Options options = new BitmapFactory.Options();
//只解析图片的宽高信息,不在内存中申请空间
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground,options);
int width = options.outWidth;
int height = options.outHeight;
//获取采样率
options.inJustDecodeBounds = false;
options.inSampleSize = 4;
//加载图片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground, options);
options.inBitmap = bitmap;
//使用inBitmap的时候inMutable要设置为true,才能够重用bitmap
options.inMutable = true;
如果不想压缩图片的话,可以使用BitmapRegionDecoder
4.拍照并且保存图片到本地
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_four_th);
imageView = (ImageView) findViewById(R.id.imageview);
public void start(View view)
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File dir = new File(Environment.getExternalStorageDirectory(),"myimg");
if (!dir.exists())
dir.mkdirs();
long fileName = System.currentTimeMillis();
file = new File(dir,fileName + ".jpg");
if (!file.exists())
try
file.createNewFile();
catch (IOException e)
e.printStackTrace();
//指定拍照后图片保存地址
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
startActivityForResult(intent,1);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
if(resultCode == RESULT_OK && requestCode == 1)
String filePath = file.getAbsolutePath();
//解析原始图片,比较大
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
//获取压缩后的图片,采样率
Bitmap smallBitmap = getSmallBitmap(file, 500, 500);
imageView.setImageBitmap(bitmap);
public Bitmap getSmallBitmap(File file,int reqWidth,int reqHeight)
String filePath = file.getAbsolutePath();
BitmapFactory.Options options = new BitmapFactory.Options();
//只是解析尺寸信息,不加载到内存中
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath,options);//此时返回bitmap为null
//计算采样率
options.inSampleSize = caculateInSampleSize(options,reqWidth,reqHeight);
//真正去加载图片
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
try
//质量压缩,压缩图片到本地,只改变存储在磁盘上的大小,bitmap的大小不会变,质量压缩不会改变像素
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bitmap.compress(Bitmap.CompressFormat.JPEG,80,bos);
return bitmap;
catch (FileNotFoundException e)
e.printStackTrace();
return null;
/**
* 计算采用率
*/
public int caculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight)
int outWidth = options.outWidth;
int outHeight = options.outHeight;
int inSampleSize = 1;
if (outWidth > reqWidth || outHeight > reqHeight)
int widthRatio = Math.round((float) outWidth/(float) reqWidth);
int heightRatio = Math.round((float) outHeight/(float) reqHeight);
//返回比例小的一个
inSampleSize = widthRatio < heightRatio ? widthRatio : heightRatio;
return inSampleSize;
public Bitmap crossBitmap(Bitmap bitmap,String filePath)
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//质量压缩,100表示不压缩,把压缩后的数据保存到bos中
bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);
int quality = 100;
//循环判断压缩后的图片大小是否大于100kb,大于继续压缩
while(bos.toByteArray().length/1024 > 100)
bos.reset();//清空bos
quality -= 10;
bitmap.compress(Bitmap.CompressFormat.JPEG,quality,bos);
//压缩好写到文件中
try
FileOutputStream fos = new FileOutputStream(filePath);
fos.write(bos.toByteArray());
fos.flush();
fos.close();
return bitmap;
catch (Exception e)
e.printStackTrace();
return null;
5.Loader
public class DownLoadTask extends AsyncTaskLoader<String>
private static final String TAG = "DownLoadTask";
DownLoadTask(Context context)
super(context);
@Override
protected void onStartLoading()
Log.d(TAG, "onStartLoading: ");
super.onStartLoading();
forceLoad();//必须调用此方法,loadINBackground方法才能执行
@Override
public String loadInBackground()
Log.d(TAG, "loadInBackground: " + Thread.currentThread().getName());
try
Thread.sleep(2000);
catch (InterruptedException e)
e.printStackTrace();
return "finish";
以上是关于Android面试题的主要内容,如果未能解决你的问题,请参考以下文章