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";
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值