Android 面试题及答案(2)

1.Activity相关。launchmode,OnSaveInstnceState,生命周期等。

4种默认的launchmode,以及OnNewIntent的回调。

OnNewIntent->OnRestart->OnStart->onResume

OnActivity->OnResume.

OnNewIntent ,OnActivityResult?

OnNewIntent launchmode with singletask or singleInstance.

but if set these two mode. OnActivityResult will be called after startActivityforResult directly。

so normally,they can not called both.

OnSaveInstanceState:

在Activity这个方法的地方有一段描述:当activity将要被kill的时候,系统会开放这个借口供程序员调用

然后它会把bundle信息传递到OnCreate & Bundle里面。

这个方法一定会在OnStop之前被调用,but 与OnPause的顺序无法确定。

某些情况下,生命周期不会走到OnDestroy,但是有些资源需要释放。这时候,最好是事件驱动。

2.Fragment相关技术。

Fragment 的生命周期:Android四大组件之Activity & Fragement

Fragment和activity沟通,

fragmentmaneger.findFragmentById

fragmentmaneger.findFragmentByTag,

fragment之间的沟通,setArgument。

getActivity & addlistener等。

数据存储和恢复。onSaveInstanceState:Fragment的该func同Activity同步。

FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务

transaction.add() 

往Activity中添加一个Fragment

transaction.remove()

从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。

transaction.replace()

使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

transaction.hide()

隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

transaction.show()

显示之前隐藏的Fragment

detach()

会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach()

重建view视图,附加到UI上并显示。

transatcion.commit()//提交一个事务

注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。

上述,基本是操作Fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。

值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。

其他问题:Android Fragment 真正的完全解析(下)

FragmentTransaction.addToBackStack(String):如果是replace的方法,那么当前fragment会被remove掉,所有back的时候,fragment的状态不会保留,只会重新

创建。而如果是hide & add的方法,就会把之前的fragment重新attch上。

如果activity管理fragment,可以调用fragment的所有public方法。

可以通过findfragmentbyid & findfragmentbytag来定位fragment,然后同上。

fragment可以通过getactivity获得实例。

当屏幕旋转是,fragment会被重复创建,这是时候如果使用OnCreate的bundle参数,就可以判断是否是第一次创建。

activity的默认实现bundle参数是有内容的。

3.序列化技术,Parcable & Serializable.

android 进程间通信数据(一)------parcel的起源

为什么要序列化:为了跨进程通信,应为进程间通信就是二进制格式,所以序列化可以通过反序列化,把数据还原。

parcable是内存机的序列化,所以速度快。

4.fastJson & GSon的使用

package com.joyfulmath.publibrary.json;

import com.alibaba.fastjson.JSON;
import com.joyfulmath.publicutils.utils.TraceLog;

/**
 * @author deman.lu
 * @version on 2016-03-24 17:20
 * 使用fastjson 来解析该问题
 */
public class JsonUtils {

    public static <T> T parseObject(String jsonStr, Class<T> entityClass)
    {
        T ret  = null;
        try {
            ret = JSON.parseObject(jsonStr,entityClass);
        }catch (Exception e)
        {
            TraceLog.e("parseObject-something Exception with:" + e.toString());
        }

        return ret;
    }

    public static String toJSONString(Object obj) {
        String ret = null;

        try {
            ret = JSON.toJSONString(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return ret;
    }
}

fastJson

5.多线程相关,包括handler,message looper. ExecutorService.

消息机制:android 进程/线程管理(一)----消息机制的框架 请看这个系列。

ExecutorService:线程池,可以提供关闭操作,等待关闭和直接关闭。

6.ImageLoader 原理,包括Glide,picasso,以及Fresco.

Picasso,square公司出品的图片加载器,内部使用OKHttp执行网络请求。

可以支持URL,FILE,数据库等路径的加载。图片不会缩放,

Picasso & Glide

首先Glide是基于Picasso的二次开发

with参数:Glide可以使用Activity、fragment等。所以它的加载可以和actiivty的生命周期绑定。

Glide默认图片ARGB_565 piocasso:ARGB_8888

Glide loading Gif but Picasso cannot. but Glide will using much memory with gif.

Glide will cache with size of imageview, so each size of imageview will contains one copy.

Picasso will only has one cahce copy with fullsize. so glide is faster than picasso.

Glide 有助于减少OOM的发生。

/**
 * Created by deman on 2016/3/30.
 */
public interface IImageLoader {
    void initLoader(Context context);
    void load(String path, ImageView view, int width, int height);
    void load(String path, ImageView view);
}

IImageLoader

public class ImageLoadManger {
    private IImageLoader iImageLoader = null;

    private static ImageLoadManger sInstance = null;

    public static synchronized ImageLoadManger getsInstance() {

        if (sInstance == null) {
            sInstance = new ImageLoadManger();
        }
        return sInstance;
    }

    /**
     *  initManager
     * @param context context
     */
    public void initManager(Context context) {
        iImageLoader = new PicassoImageLoader();
        iImageLoader.initLoader(context);
    }

    /**
     * load image with path
     * @param path          path
     * @param imageView imageView
     */
    public void load(String path, ImageView imageView) {

        if(null == iImageLoader)
        {
            throw new RuntimeException("initManager has not been init");
        }
        iImageLoader.load(path, imageView);
    }

    /**
     * load image with path
     * @param path                  path
     * @param imageView         imageView
     * @param width                 width
     * @param height                height
     */
    public void load(String path, ImageView imageView,int width,int height) {
        if(null == iImageLoader)
        {
            throw new RuntimeException("initManager has not been init");
        }
        iImageLoader.load(path, imageView,width,height);
    }
}

ImageLoadManger

public class PicassoImageLoader implements IImageLoader {

    Picasso picasso;
    Context context;

    private void init()
    {
        picasso = new Picasso.Builder(context).build();
    }

    @Override
    public void initLoader(Context context) {
        this.context = context;
        init();
    }

    @Override
    public void load(String path, ImageView view,int width,int height)
    {
        if(null == path)
        {
            throw new RuntimeException("uri is null!");
        }

        if(width == 0 || height == 0)
        {
            throw new RuntimeException("width & height is 0");
        }

        picasso.load(path)
                .resize(width, height)
                .into(view);
    }

    @Override
    public void load(String path, ImageView view) {
        if(null == path)
        {
            throw new RuntimeException("uri is null!");
        }

        picasso.load(path)
                .into(view);
    }
}

PicassoImageLoader

Glide 500K,Picasso 100K

Fresco,是facebook开源的一套图片加载框架。它的使用是基于view级别的。SimpleDraweeView

Fresco, image pipline技术,三级缓存。所以不容易oom。

第一层。bitmap缓存:在5.0上,使用java heap,5.0一下,使用ashme内存,减少GC发生的概率。

app到后台,bitmap缓存被清空。

第二层,内存缓存。存放原始的压缩图片。

第三层,磁盘缓存,不会被清空。

Fresco对内存的节省效果很大,尤其是低端机,可以减少OOM的几率。

包比较大,而且使用起来也复杂。

7.Adapter 和 ListView 组合。

8.用户Cookie设计。token的流程包括登陆流程。

登陆设计:

1)登陆跳转

首先是登陆到指定页面,比如MainActivity,

还有就是登陆后,回到上一页。

这2中需要在登陆的时候考虑到。

2)登陆的密码验证

密码加密后传输,以参数的形式。

API使用https请求。

登陆成功后,需要把userinfo存到本地,以便退出后,可以自动登陆。

3)自动登陆

token机制。每次登陆成功后,本地保存一个token信息,同时token可以保存过期的信息机制。

token过期后,所有先关API(需要验证token信息的),都会返回相同的code信息,然后统一处理。

弹出AlertDialog,让用户去判断是否需要登陆。

9.网络请求的封装。

Volley

Volley+okhttp

Retrofit

其中Retrofit+okHttp+Picasso 是square公司统一出品的。

主流的套路是,把每个框架都分开了,这样便于替换。

Retrofit+okHttp /Volley+Okhttp   add  fastjson , Glide/Fresco + buttnkinfe/dragger2/androidannotation + Rebotim.

Android 框架学习之 第一天 okhttp & Retrofit

10.Android & H5 的交互。

H5调用andoriod代码:

webSettings.setJavaScriptEnabled(true);

mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");

js回调,@JavascriptInterface

android 调用JS

mWebView.loadUrl("javascript:wave()");

跳转协议:

当JS回调的时候,可以在shouldOverrideUrlLoading的时候

把URL配置成一个协议。

11.代码混淆。

混淆的开启:androidstudio:

在gradle里面,有

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘
        }
    }

关键是minifyEnabled 编程true,混淆编译就会打开。

一般需要配置proguard-rules.pro

proguard4大过程:

压缩,字节码优化,混淆,预检。

proguard 可以配置版本:

proguard在sdk里面。

proguard可以配置过程:

-dontshrink 不压缩输入的类文件

-dontoptimize 不优化输入的类文件

-dontobfuscate 不混淆输入的类文件

12.android打包机制

包括sdk的命令。

Android原理揭秘之APk生成过程

打包过程:aapt用来生成资源文件R.java

java编译器->class文件,然后dex编译成.dex文件。

aapt放置的resource

打包成apk文件。

其他的最终会生成dex文件。+sign 过程,就是APK,当然可能需要压缩优化。

13.线上Crash分析并修复的能力。

首先是,对于没有处理的crash地方,通过UnCaughtExceptionHandler 来处理和收集日志。

三步:

收集日志并发送到服务器

提示用户处理

保存到SD

需要把proguardmapping文件保存下来,以便线上app分析问题。

14.内存泄露,包括内存优化,内存泄露的场景,以及MAT工具的使用。

请参见Android 高级面试题及答案

15.Monkey机制。 Android开发如何对一款App进行monkey测试。

monkey就是对屏幕进行随机事件发送,一般次数都在1W次以上,主要测试结果是crash,ANR的事件。

monkey对象是apk

monkey的事件流是随机的。

monkey的文档:http://developer.android.com/guide/developing/tools/monkey.html

16.调试工具,包括DDMS,android studio的调试功能。

androidstudio工具:

AndroidMonitor:StartMethodTracing 测试method执行时间。

DDMS也有traceView,功能是一样的。

测试View的布局:hierachyviewer.

debug:高级调试功能:

Evaluate Expression:在run菜单下面,有这个选项,它的功能同watch差不多,但是它可以测试执行语句。

条件断点:在循环里面,你只对i=9感兴趣,你怎么办,在断点的位置,右击,出现条件。

日志断点:也是一个非常实用的技能,当我们某些情况下需要打印日志,但是每次编译非常耗时,这个时候,我们实用日志断点,可以打印日志,而

不修改源代码。在Debug窗口里面的console里面可以看到打印的日志。

方法断点:方法断点就是在函数名的这一样,断点,它会在函数入口和结束的地方断点。

异常断点:就是Run->View blockpoint->javaException.

属性断点:就是在某个属性被修改或者访问的时候,但是多线程复杂环境下,很难发现。Run->View blockpoint->javaFieldWatchpoints.

DDMS:Android Studio中怎么使用DDMS工具?

17.单元测试,Junit的使用,包括其他测试框架。

public class ExampleTest extends InstrumentationTestCase {

    public void test() throws Exception {
        final int expected = 1;
        final int reality = 1;
        assertEquals(expected, reality);
    }

    public void test_2() throws Exception{
        final int expected = 2;
        final int reality = 3;
        assertEquals(expected,reality);
    }
}

需要在configration里面配置。

18.GIT的高级功能,Stage,rebase,revert,stash,cherry pick & sub module

stage是index区, 一个概念,对应work区 和history区

revert:就是把某次历史提交给取消掉,but它的提交记录会被保留,也就是可以提交到远程库。

reset:只是回退历史,回退内容可以被保留在work区域。

cherry pick:branch2 上的某条修改,需要merge到branch3上,but我只是需要这一条修改,用这条指令。

sub module:用处不是很大。

19.插件化编程。

专开一篇讲解

20.设计模式,对常见设计模式的熟悉程度。

TBD.

21.热修复技术。

TBD.

22.React +Native框架

TBD.

23.消息推送原理

TBD.

时间: 2024-10-22 11:08:41

Android 面试题及答案(2)的相关文章

常见Android面试题及答案(详细整理)

常见Android面试题及答案(详细整理) 1. 请描述一下Activity 生命周期. 答: 如下图所示.共有七个周期函数,按顺序分别是: onCreate(), onStart(), onRestart(), onResume(), onPause(),onStop(), onDestroy(). onCreate(): 创建Activity时调用,设置在该方法中,还以Bundle的形式提供对以前存储的任何状态的访问. onStart(): Activity变为在屏幕上对用户可见时调用. o

【转】 71道经典Android面试题和答案,重要知识点都包含了

,,面试题1.        下列哪些语句关于内存回收的说明是正确的? (b ) A. 程序员必须创建一个线程来释放内存  B.内存回收程序负责释放无用内存   C.内存回收程序允许程序员直接释放内存   D.内存回收程序可以在指定的时间释放内存对象 2.        下面异常是属于Runtime Exception 的是(abcd)(多选)      A.ArithmeticException      B.IllegalArgumentException       C.NullPoint

2018年Android面试题含答案(上)

这些面试题是我在今年年初换工作的时候整理,没有重点.包括java基础,数据结构,网络,Android相关等等.适合中高级工程师.由于内容过多,将会分为上下两部分.希望能够帮到一些朋友,如果帮助到你,希望能够点个赞.没有单独分出来,面试题目都是穿插的.因为有些事外面试过程中遇到的,我就又加上去了.总之你弄懂了这些,基本是没有问题了.如果是bat那些企业,你还得准备算法,jvm这些知识.好了,废话不多说了. 1.java中==和equals和hashCode的区别  基本数据类型的==比较的值相等.

Android面试题及其答案(三)

1.list map set三个接口,存取元素时,各有什么特点? List与Set都是单列元素的集合,它们有一个功共同的父接口Collection. Set里面不允许有重复的元素. 存元素:add方法有一个boolean的返回值,当集合中没有某个元素,此时add方法可成功加入该元素时,则返回true:当集合含有与某个元素equals相等的元素时,此时add方法无法加入该元素,返回结果为false. 取元素:没法说取第几个,只能以Iterator接口取得所有的元素,再逐一遍历各个元素. List表

android面试题及答案

JAVA 1.GC是什么? 为什么要有GC? GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法. 2.XML包括哪些解释技术,区别是什么? DOM和SAX DOM将文档解析成一颗文档树,可在节点上进行遍历.增加.修改和删除.一次性读入内存,对内存消耗大. SAX至上

Android 面试题(答案最全)

1. Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念DVM指dalivk的虚拟机.每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例.而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念.2.sim卡的EF 文件有何作用sim卡的文件系统有自己规范,主要是为了和手机通讯,sim本 身可以有自己的操作系统,EF就是作存储并和手机通讯用的3.嵌入式操作系统内存管理有哪几种, 各有何特性页式,段式,段页,用到

Android面试题及其答案(一)

handler vs AsyncTask AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程. 优点: l  简单,快捷 l  过程可控 缺点: l 在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来 Handler 异步实现时,涉及到 Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动T

Android面试题及其答案(二)

内存相关的问题在面试中被问到的概率还是比较大的,而且内存优化对于一个程序的性能而言也是至关重要的,现在就让我们一起来学习吧! 不废话,直接上干货~ 一.内存泄漏 内存泄漏就是我们对某一内存空间的使用完成后没有释放. 主要原因:导致内存泄漏最主要的原因就是某些长存对象持有了一些其它应该被回收的对象的引用,导致垃圾回收器无法去回收掉这些对象. 出现的场景: 1.数据库的cursor没有关闭: 2.构造adapter时,没有使用缓存contentview: 3.Bitmap对象不使用时采用recycl

2018年Android面试题含答案--适合中高级(下)

1.Activity生命周期? onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy() 2.Service生命周期? service 启动方式有两种,一种是通过startService()方式进行启动,另一种是通过bindService()方式进行启动.不同的启动方式他们的生命周期是不一样. 通过startService()这种方式启动的service,生命周期是这样:调用sta