Android开发常见问题汇总

我们两清 提交于 2020-01-01 22:33:54

文章目录

1.Activity的启动模式

1.什么是Activity启动的标准模式?都会经历那些生命周期的函数?

标准模式是指用户和Activity之间正常交互所所经历的生命周期

  • onCreate:第一次被被打开的时候
  • onStart:被看见的时候
  • onRestart:当再次被打开的时候
  • onResume:当获取用户焦点的时候
  • onPause:失去用户焦点的时候
  • onStop:当被完全遮挡的时候
  • onDestroy:当被销毁的时候

2.什么是Activity启动的异常模式?都会经历那些生命周期?

当内存不够用或系统配置发生变化的时候Activity会销毁并重启,系统配置发生变化主要是指手机横竖屏发生变化,软件盘的隐藏等情况

  • 1.onSaveInstanceState
  • 2.onStop
  • 3.onDestroy
  • 4.onCreate
  • 5.onStart
  • 6.onRestoreInstanceState

3.怎么指定Activity在横竖屏切换的时候不敏感?

通过在清单文件里面给Activity设置configChanges属性即可,具体配置如下:android:configChanges=“orientation|keyboardHidden|screenSize”
默认情况下Activity在横竖屏切换时会重启,配置了此属性Activity会横竖屏切换,但是不会重启

4.怎么为Activity指定为横屏或竖屏?

给Activity配置android:screenOrientation="portrait"属性即可

5.Activity启动都有哪些模式?在清单文件里面配置和在代码里面指定到底有何不同?

standard:标准模式
singleTop:栈顶复用模式
singleTask:栈内复用模式
singleInstance:单例模式
其中标准模式和栈顶复用模、单例模式比较好理解,栈内复用模式稍显复杂,在清单文件里面不能配置clearTop属性,在代码里面不能指定singleInstance模式

6.前台任务栈有Activity:AB,后台任务栈有Activity:CD,且CD的启动模式都是singleTask,现在启动Activity D,此时后退任务栈的的顺序是什么?

7.怎么给Activity指定单独的任务栈?怎么查看任务栈的?

在AndroidMinifest.xml通过Activity的taskAffinity属性可以直接指定它所在任务栈,但是该属性必须要和singleTask模式配合使用才能起作用,否则配置无效

  • adb shell dumpsys activity 查看任务栈

8.Activity的启动意图都有哪些?

  • 显示意图:明确指定要启动的组件的类名和包名
  • 隐式意图:没有明确指定要启动组件的类名和包名

9.隐式意图需要配置目标组件的InterFilter,这个过滤器都有哪些信息?配置都有哪些规则?

  • action:请求目标组建所要完成的动作,可以配置多个
  • category:请求目标组件所附加的类别信息,可以配置多个
  • data:完成这个的动作所要的数据,包括两部分:URI和mimeType可以配置多个
  • URI的标准结构:😕/:/[||]
    配置完毕通过Intent打开目标组件的时候,必须要配置一个Action且能匹配上,Category可以不用配置,如果配了则必须要匹配上,否则也会打开失败
    Intent会默认添加上android.intent.category.default这个Category,URI默认的scheme为content或file
    ==========================================================

2.跨进程通信

1.在Android开发中怎么使用多进程?

只能在AndroidMinifest.xml中给四大组件通过配置android:progress属性来指定它所在的进程,也就是说无法给某一个线程或者具体的实体类来指定它所在的进程

  • 方式1:“:remote”
  • 方式2:“com.android.test”
    有冒号的表示这是当前应用的私有进程,其他应用的组件不可以和它运行在同一个进程中,不带冒号的表示他是一个全局进程其他应用的组件可以和它跑在同一个进程中,可以通过adb shell ps来查看进程

2.同一个应用中开启了多进程会造成哪些问题?

  • 静态成员失效
  • 单例失效
  • 线程同步机制失效
  • SharedPreference的可靠性会降低
  • Application会多次被创建

2.怎么通过Serializable来序列化和反序列化一个对象?

    //序列化过程 
    User user = new User(0,"jake",true); 
    ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream("cache.txt")); 
    out.writeObject(user); 
    out.close(); 
    //反序列化过程 
    ObjectInputStream in = new ObjectInputStream( new FileInputStream("cache.txt")); 
    User newUser = (User) in.readObject();
    in.close();

3.Parcelable和Serializable他们的作用是什么?有什么区别?

通过这两个接口可以完成对象的序列化,因为只有通过序列化的对象才可以通过Intent和Binder实现对象的跨进程传输

  • Serializable是磁盘序列化,java提供的,使用方便,但是开销大
  • Parcelable是内存序列化,Android提供的,使用复杂,但是效率很高

4.什么是Binder?什么是Binder的死亡代理?

Binder它是Android给我们提供的跨进程通信手段,可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder
Binder的死亡代理是指,当服务端由于意外终止运行,此时客户端可以通过死亡代理得到通知而删除之前的代理,对服务进行重新绑定

 private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mBookManager == null) {
                return;
            }
            mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mBookManager = null;
            // TODO:这里重新绑定远程Service 
        }
    };
    mService=IMessageBoxManager.Stub.asInterface(binder);
    binder.linkToDeath(mDeathRecipient,0);

5.什么是AIDL?

AIDL的全称是Android Interface Description Language,即Android接口描述语言,它是Android给我们提供的一个工具,通过他可以很方便的生成IPC代码,实现跨进程通信。

6.Android中的跨进程通信方式都有哪些?

  • 使用Bundle
  • 使用共享文件
  • 使用Messenger
  • 使用AIDL
  • 使用ContentProvider
  • 使用Socket

7.什么Binder连接池?

Binder连接池就是将多个Binder服务通过一个接口服务来提供,避免了创建多个Service的问题

=====================================================

3.View的事件体系

1.什么是View?什么是ViewGroup?他们关系如何?

View表示一个控件,它具有用户界面显示功能,不管是简单的TextView、Button,还是复杂的LinearLayout、ListView
他们共同的基类都是View,ViewGroup表示一个控件组,ViewGroup继承于View,这就意味着View不但可以表示一个控件,也可以表示多个控件组成的控件组

2.View都有哪些表示位置的参数?

  • left:左上角的横坐标
  • top:左上角的纵坐标
  • right:右下角的横坐标
  • bottom:右下角的纵坐标
这些坐标位置都是相对于父容器而言的,是一种相对坐标,是不可变的原始坐标
  • width = right - left
  • height = bottom - top
  • x:也表示左上角的横坐标,可变的
  • y: 也表示左上角的纵坐标,可变的
  • translationX:View左上角相对于父容器的在x轴上的偏移距离
  • translationY:View左上角相对于父容器的在y轴上的偏移距离
  • x = left + translationX
  • y = top + translationY

4.MotionEvent是什么?

MotionEvent表示手指触摸屏幕所产生的一系列事件。

  • ACION_DOWN:表示手指触摸屏幕的一瞬间
  • ACTION_UP:表示手指离开屏幕的一瞬间
  • ACTION_MOVE: 表示手指在屏幕上的移动
    我们可以通过MotionEvent获取手指在屏幕上触摸的位置的坐标
  • getX(): 表示相对于View左上角的横坐标
  • getY(): 表示相对于View左上角的纵坐标
  • getRawX(): 表示相对于屏幕左上角的横坐标
  • getRawY(): 表示相对于屏幕左上角的纵坐标

5.TouchSlop表示什么?

TouchSlop它是一个常量,表示系统所能识别的最小滑动距离,它和设备有关,不同的设备可能这个值大小可能不一样

  • ViewConfiguration.get(getContext()).getScaledTouchSlop()。

6.VelocityTracker是什么?

VelocityTracker表示手指在滑动过程中的速度,包括水平方向的速度和垂直方向的速度

    VelocityTracker velocityTracker = VelocityTracker.obtain();
    velocityTracker.addMovement(event);
    velocityTracker.computeCurrentVelocity(1000); 
    int xVelocity = (int) velocityTracker.getXVelocity(); 
    int yVelocity = (int) velocityTracker.getYVelocity();
    velocityTracker.clear(); 
    velocityTracker.recycle();

7.GestureDetector是什么?

GestureDetector是一个手势检测器,用来识别用户的手势行为,如单击、双击、长按、快速滑动、拖动等

    GestureDetector mGestureDetector = new GestureDetector(this); 
    //解决长按屏幕后无法拖动的现象
    mGestureDetector.setIsLongpressEnabled(false);
    boolean consume = mGestureDetector.onTouchEvent(event); 
    return consume;

8.Scroller是什么?它的工作原理?

通过Scroller可以实现View的平滑移动,Scroller本身并不能实现平滑移动,它必须借助View的computeScroll方法才能实现平滑移动
它的标准模板代码如下:

    Scroller scroller = new Scroller(mContext); 
    // 缓慢滚动到指定位置 
    private void smoothScrollTo(int destX,int destY) { 
        int scrollX = getScrollX(); 
        int delta = destX -scrollX; 
        // 1000ms内滑向destX,效果就是慢慢滑动 
        mScroller.startScroll(scrollX,0,delta,0,1000); 
        invalidate(); 
    }
    @Override public void computeScroll() { 
        if (mScroller.computeScrollOffset()) { 
            scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); 
            postInvalidate(); 
        } 
    }

9.View的scrollTo和scrollBy有何区别?

  • scrollTo是绝对移动
  • scrollBy是相对移动
    scrollBy内部调用了scrollTo

10.View的mScrollX和mScrollY分别表示什么,正负何如?

  • mScrollX:表示View的左边缘和View内容左边缘在水平方向上的距离

  • mScrollY: 表示View的上边缘和View内容的上边缘在垂直方向上的距离

  • mScrollX+:表示View的左边缘在View内容左边缘的右边,反之亦然

  • mScrollY+:表示View的上边缘在View内容上边缘的下边,反之亦然

11.实现View的移动都有哪些方法?他们的区别是什么?

  • scrollTo/scrollBy: 操作简单,适合View内容的移动
  • 动画:操作简单,可以实现复杂的移动,移动是View本身
    ObjectAnimator.ofFloat(targetView,"translationX",0,100).setDuration(100).start();
    //==================================
    final int startX = 0; 
    final int deltaX = 100; 
    ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000); 
    animator.addUpdateListener(new AnimatorUpdateListener() { 
        @Override public void onAnimationUpdate(ValueAnimator animator) { 
            float fraction = animator.getAnimatedFraction(); 
            mButton1.scrollTo(startX + (int) (deltaX * fraction),0); 
        }
    }); 
    animator.start();
  • 改变布局参数:操作复杂
    MarginLayoutParams params = (MarginLayoutParams)mButton1.getLayoutParams(); 
    params.width += 100; params.leftMargin += 100; 
    mButton1.requestLayout(); 
    //或者mButton1.setLayoutParams(params);

12.简述View事件分发流程?

13.View的滑动冲突都有哪些场景?怎么解决?

  • 内部滑动和外部滑动方向一致

  • 内部滑动和外部滑动方向不一致

  • 上述两种情况的嵌套

  • 外部拦截法

  • 内部拦截法
    ==========================================================

4.View的工作原理

ViewRoot的作用是什么?

ViewRoot对应的是ViewRootImpl,它是连接WindowManager和DecorView的纽带,View绘制的三大流程都是通过ViewRoot来完成的。

MeasureSpec是什么?

MeasureSpec包含了父控件对子控件的布局要求,他是一个一个32位整形数字,前两位表示测试模式,后三十位为表示测量大小

  • UNSPECIFIED:未定义
  • EXACTLY: 精确的
  • AT_MOST: 至多的

简述View绘制的三大流程?

  • onMeasure:确定控件的大小
  • onLayout:确定控件的位置
  • onDraw:绘制控件

简述View的draw的过程?

  • 1.绘制背景
  • 2.如果必要锁定画布
  • 3.绘制View的内容
  • 4.绘制子View
  • 5.如果必要恢复画布
  • 6.绘制View的装饰

自定义View的分类?

  • 1.继承View
  • 2.组合View
  • 3.自绘View
    =============================================================

5.Window的工作原理

1.Window和WindowManager的实现类?

  • Window的实现类为PhoneWindow
  • WindowManger的实现类为WindowManagerImpl

2.怎么想屏幕上添加一个Window?

    mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
    public void onButtonClick(View v) {
        if (v == mCreateWindowButton) {
            mFloatingButton = new Button(this);
            mFloatingButton.setText("click me");
            mLayoutParams = new WindowManager.LayoutParams(
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
                    PixelFormat.TRANSPARENT);
            mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_SHOW_WHEN_LOCKED;
            mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
            mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
            mLayoutParams.x = 100;
            mLayoutParams.y = 300;
            mFloatingButton.setOnTouchListener(this);
            mWindowManager.addView(mFloatingButton, mLayoutParams);
        }
    }
  • FLAG_NOT_FOCUSABLE: Window不需要获取焦点
  • FLAG_NOT_TOUCH_MODAL:Window区域类的事件自己处理,区域外的事件传递给底层的Window处理
  • FLAG_SHOW_WHEN_LOCKED:开启此模式,Window可以显示在锁屏的界面上

3.Window都有哪些分类?以及他们的层级范围?

  • 应用Window:一个Activity对应一个应用Window,1-99
  • 子Window:子Window必须依附在父Window之上,如Dialog,1000-1999
  • 系统Window:如Toast、菜单、状态栏都属于系统Window,2000-2999

4.WindowManager都提供哪些基本的操作?

  • 添加View:addView(View view,ViewGroup.LayoutParams params);
  • 修改View:updateViewLayout(View view,ViewGroup.LayoutParams params);
  • 删除View:removeView(View view);
    从Window提供的操作方法可以看出,Window的本质上是也是View

5.Window的添加、删除、更新过程?Window和View的关系?

Window最后操作都是由ViewRootImpl完成的,View不能单独存在,必须依附在Window这个抽象的概念之上,所以有View的地方就有Window

6.Activity的Window创建过程?

首先将View添加到DecorView,然后把DecorView添加到Window

6.Dialog的Window创建过程?

注意创建Dialog时候必须要用Activity的Context,如果用Application就会报错

6.Toast的Window创建过程?

7.除了常见的Activity,Dialog、Toast还有哪些Window?

PopupWindow、菜单、状态栏其底层都是通过Window来实现的

=============================================================

6.四大组件的工作过程

1.Activity的启动过程?

2.Service的启动过程?

3.Service的绑定过程?

4.BroadCastReceiver的注册过程?

5.广播的发送和接收过程?

6.ContentProvider的注册过程?

7.ContentResolver的调用流程?

==========================================================

7.Android的消息机制

1.Handler是什么?工作流程?

Handler是Android系统给我们提供的一个消息管理器,主要用来实现线程切换,消息的发送和接收

2.ThreadLocal的作用?

它可以保证不同线程不受干扰的保存和提供数据

3.怎么实现一个异步消息处理线程?

4.主线程中的消息循环是怎么实现的?

==========================================================

8.Android中的线程和线程池

1.HandlerThread的基本使用?

HandlerThread是一个异步消息处理线程

HandlerThread handlerThread = new HandlerThread("handler-thread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                Log.v("MYTAG","handleMessage what:"+msg.what+";currThread:"+Thread.currentThread().getName());
            }
        };

2.IntentService的基本使用?

IntentService是一个后台服务线程,IntentService内部封装了Handler和HanlderThread

public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.v("MYTAG","onHandleIntent currThread:"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
startService(new Intent(this, MyIntentService.class));
startService(new Intent(this, MyIntentService.class));
startService(new Intent(this, MyIntentService.class));

3.AsyncTask的execute方法和executeOnExecutor方法有何区别?

AsyncTask是一个便于在子线程中访问UI线程的一个线程池

 public class MyAsyncTask extends AsyncTask<String,Integer,String> {
    public String taskName;

    public MyAsyncTask(String taskName) {
        super();
        this.taskName = taskName;
    }

    @Override
    protected String doInBackground(String... strings) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return taskName;
    }

    @Override
    protected void onPostExecute(String result) {
        super.onPostExecute(result);
        Log.e("MYTAG", "result:"+result);
    }
}

  • execute:串行执行
  new MyAsyncTask("AsyncTask1").execute("");
  new MyAsyncTask("AsyncTask2").execute("");
  new MyAsyncTask("AsyncTask3").execute("");
  • executeOnExecutor:并行执行
  new MyAsyncTask("AsyncTask1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
  new MyAsyncTask("AsyncTask2").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
  new MyAsyncTask("AsyncTask3").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");

4.什么是线程池?

线程池是多线程的一种处理形式,处理过程中把任务添加到任务队列,然后创建线程自动执行这些任务,这些线程是可以重复使用的,有效的避免了线程重复的创建和销毁所带来的性能上的消耗

5.Java中的线程池?工作原理

ThreadPoolExecutor是Java给我们提供的线程池,它有6个参数

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程空闲时的超时时长
  • unit:超时时长的单位
  • workQueue:工作的任务队列
  • threadFactory:创建线程的工厂
    ThreadPoolExecutor的工作原理是:
  • 如果线程池中线程数量没有超过核心线程数,那么每来一个任务会创建一个核心线程来执行任务
  • 如果线程池中的线程数超过了核心线程数,那么每来一个任务会创建一个非核心线程来执行任务
  • 如果线程池中的线程数超过了最大线程数,那么所来的任务会在任务队列中排队,如果任务队列满了,它会拒绝执行

5.Java都给我们提供了哪些线程池?

  • a.FixedThreadPool:线程数固定的线程池,只有核心线程数,没有超时限制,任务队列大小也没有限制
 ExecutorService executorService = Executors.newFixedThreadPool(3);
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
 }
  • b.CachedThreadPool:它是一种线程数量不定的线程池,它只有非核心线程,并且其最大线程数为Integer.MAX_VALUE
ExecutorService executorService1 = Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
  • c.ScheduledThreadPool:它的核心线程数是固定的,非核心线程数没有限制,并且非核心线程空闲时,会立马收回
    用于执行延迟、周期性的重复任务
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
scheduledExecutorService.scheduleAtFixedRate(): 固定频率执行
scheduledExecutorService.scheduleWithFixedDelay(): 固定频率执行,前提是必须前一个任务执行完毕才行执行下一个
  • d.SingleThreadExecutor:该线程池只有一个核心线程,各个任务之间不需要处理线程同步问题
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

==========================================================

9.Android中的缓存

1.LruCache的基本使用?

int cacheSize = 4 * 1024 * 1024; // 4MiB
LruCache<String, Bitmap> mMemoryCache= new LruCache<String, Bitmap>(cacheSize) {
       protected int sizeOf(String key, Bitmap value) {
           return value.getByteCount();
       }
}
mMemoryCache.put(key,bitmap);
mMemoryCache.get(key);

2.DiskLruCache的基本用法?

DiskLruCache  mDiskLruCache = DiskLruCache.open(Environment.getDownloadCacheDirectory(), 100, 1, 50 * 1024 * 1024);
private void putCache(String key) {
        File forder = Environment.getDownloadCacheDirectory();
        try {
            mDiskLruCache = DiskLruCache.open(forder, 100, 1, 50 * 1024 * 1024);
            //注释1
            DiskLruCache.Editor editor = mDiskLruCache.edit(key);
            if (editor != null) {
            	 //注释2
                OutputStream outputStream = editor.newOutputStream(0);
                 //注释3
                if (downloadBitMapByUrl(key, outputStream)) {
                	//注释4
                    editor.commit();
                } else {
                    editor.abort();
                }
                mDiskLruCache.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
public void getCache(String key){
        Bitmap bitmap = null;
        DiskLruCache.Snapshot snapShot = null;
        try {
            snapShot = mDiskLruCache.get(key);
            if (snapShot != null) {
                FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(0);
                //注释1
                FileDescriptor fileDescriptor = fileInputStream.getFD();
                bitmap = new ImageResizer().decodeSampledBitmapFromFileDescriptor(fileDescriptor,100,100); 
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

3.ImageLoader的工作原理?
首先尝试从内存缓存中读取图片,接着尝试从磁盘缓存中读取图片,最后才从网络中拉取图片。另外,这个方法不能在主线程中调用,否则就抛出异常。这个执行环境的检查是在loadBitmapFromHttp中实现的,通过检查当前线程的Looper是否为主线程的Looper来判断当前线程是否是主线程

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!