关于Android基础面试,你需要知道的(学习笔记 一)

关于

前段时间找了两周工作(天太冷了不想跑路去公司面试,所以没怎么上心找),最终也是有三家公司给了我offer(一家上司外包公司(工资是12.5k)没去)、(上市公司做自研项目(9k)还是要的太少了,所以没去)、(自研公司行业项目(工资保密))。
应届生找工作的话基础很重要,尽量找自研公司。外包公司(中软、润和等)不建议去,虽然工资高,但是基本打杂。
以上仅代表个人想法,勿喷

Android四大组件

Activity

Activity的生命周期。

在这里插入图片描述

一个简单的activity生命周期:onCreate()->onStart()【此时activity不可见】->onResume()【activity处于运行状态】->onPause()【有其他活动实例在该实例前面,此时处于暂停状态】->onStop()【activity不可见,activity处于停滞状态】->onRestart()【重新回到前面】或者-onDestroy()【activity生命周期结束加粗样式

  • Activity跳转到了新的Activity界面或者进入后台、锁屏等,系统会调用onPause(),然后新的Activity会onCreate()->onStart()->onResume(),然后旧的Activity会onStop(),使Activity进入停滞状态,如果此时重新回到该Activity界面(上一个Activity回来、从后台回到前台、解开锁屏),系统会调用onRestrat()->onStart()->onResume(),上一个界面onStop().
  • Activity被其他Activity覆盖的时候,系统会调用onPause()方法,暂停当前Activity的运行。
  • 当用户退出Activity时会调用onPause()->onStop()->onDestroy()结束生命周期。
  • 当Activity中弹出dialog对话框的时候,不会调用onPause()方法,只有当Activity启动了dialog风格的Activity时才会调用。
  • 当Activity由于异常情况下终止的,系统会调用onSaveInstance来保存当前Activity的状态,这个方法是在onStop()之前调用的,当Activity重新创建后,系统会调用onRestoreInstanceState(),并把Activity销毁时的onSaveInstance里面保存的bundle对象作为参数同时传递给onRestoreInstanceState和onCreate()方法。

Activity的四种启动模式

Activity有四种启动模式 standard,singleTop,singleTask,singleInstance,这四种模式我们可以在清单文件的activity节点下中的android:launchMode配置。

  • standard模式:在这种模式下,activity会默认进入启动它的activity任务栈中,这是默认模式。
  • singleTop模式:在这种模式下(栈顶复用模式)。如果新activity位于任务栈的栈顶时,activity不会被重新创建,会调用onNewIntent方法。
  • singleTask模式:栈内复用模式。只要activity在一个栈中存在,activity就不会被重新创建,同时回调onNewIntent方法。可以称为单例模式,只会存在一种,有会直接调用。
  • singleInstance模式:单实例模式。这种模式的activity只能单独的位于一个任务栈中,并让多个应用共享这个Activity实例,一旦该模式的Activity实例已经存在,其他激活该Activity时都会重用该栈中的实例(onNewIntent)。

Service

Service分为两种,一种是Service(这一种是运行在主线程中的,如果要执行耗时操作,可以在service中创建一个异步或线程来执行),一种是IntentService(这是异步服务,是继承于Service的子类)。

  • IntentService:service+thread模式,用来进行处理异步请求的服务,内部有一个工作线程,所有发送给服务的请求都会在这个工作线程中按序执行,在处理完所有请求后服务会自动停止。通过在子类onHandleIntent(Intent)接口中进行实际请求。当IntentService第一次启动的时候会调用其onCreate来完成一些初始化操作:
  • 1.创建一个HandlerThread对象,内部有一个消息循环队列的线程。
  @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

  • 2.利用已创建的HandlerThread内部的消息循环创建一个ServiceHandler对象,这样就可以在对象的线程中执行了。
 @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
  • 3.通过执行onStartCommand回调调用oonStart方法对发送过来的请求并通过mServiceHandler进行处理,在handleMessage中通过onHandleIntent进行实际处理,然后调用stopSelf()来尝试停止当前服务。、
  private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    public final void stopSelf() {
        stopSelf(-1);
    }

    /**
     * Old version of {@link #stopSelfResult} that doesn't return a result.
     *  
     * @see #stopSelfResult
     */
    public final void stopSelf(int startId) {
        if (mActivityManager == null) {
            return;
        }
        try {
            mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
    }
    
    /**
     * Stop the service if the most recent time it was started was 
     * <var>startId</var>.  This is the same as calling {@link 
     * android.content.Context#stopService} for this particular service but allows you to 
     * safely avoid stopping if there is a start request from a client that you 
     * haven't yet seen in {@link #onStart}. 
     * 
     * <p><em>Be careful about ordering of your calls to this function.</em>.
     * If you call this function with the most-recently received ID before
     * you have called it for previously received IDs, the service will be
     * immediately stopped anyway.  If you may end up processing IDs out
     * of order (such as by dispatching them on separate threads), then you
     * are responsible for stopping them in the same order you received them.</p>
     * 
     * @param startId The most recent start identifier received in {@link 
     *                #onStart}.
     * @return Returns true if the startId matches the last start request
     * and the service will be stopped, else false.
     *  
     * @see #stopSelf()
     */
    public final boolean stopSelfResult(int startId) {
        if (mActivityManager == null) {
            return false;
        }
        try {
            return mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
        return false;
    }
  • 4.在onDestroy中调用,mServiceLooper.quit()方法,停止工作线程的消息循环,等待线程退出。
 @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }`

Service的两种启动方式

  • 通过start方法开启服务
    创建一个类继承Service,其中会有一个IBinder onBind()方法。
public class TestHandlerIntentService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

还要再清单文件中注册这个服务:

 <service 
              android:name="xxService"
              android:enable="true"
              android:exported="true"/>

启动方法startService(intent);以及停止服务方法:stopService(intent);(service的生命周期和调用者无关)
当Service首次启动,先调用onCreate()->onStartConmmand()->onDestroy()方法,多次调用会执行onStartCommand()方法。

  • 通过bind的方式启动服务
    当Service首次启动,先调用onCreate()->onBind()->unBind()->onDestroy()方法,在activity中调用需要获取ServiceConnection对象,然后货哦渠道service中内部类的类对象,然后通过类对象调用类中的方法,这个类需要继承Binder对象。
public class MyServiceBinder extends Binder{
        public myservice getService(){
            return myservice.this;
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MyServiceBinder();
    }

在Activity中调用,不再使用的时候,通过unbindService(connection)方法

private  ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //活动与服务连接成功是调用
            myservice.MyServiceBinder binder =(myservice.MyServiceBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //断开调用
        }
    };

bindService()也可以调用多次,当发起对象与Service已经成功绑定后,不会多次返回ServiceConnection中的回调方法。

BroadcastReceiver

广播分为两种,一种是是无序广播,另一种是有序广播。

  • 静态注册即在清单文件中喂BroadcastReceiver进行注册,使用标签声明,并在标签内用标签设置过滤器。不受页面生命周期影响,但是会占用CPU资源。继承BroadcastReceiver,在onReceive()方法中取出Intent传递的字符。
public class NormalReceiver extends BroadcastReceiver {

    private static final String TAG = "NormalReceiver";

    public NormalReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("Msg");
        Log.e(TAG, msg);
    }

}

在Activity中调用sendBroadcast(Intent)方法

        Intent intent = new Intent(NORMAL_ACTION);
        intent.putExtra("Msg", "Hi");
        sendBroadcast(intent);

清单中配置

<receiver android:name="broadcastReceiver">
     <intent-filter>
       <action android:name="xxx.receiver"/>
       </intent-filter>
</receiver>       

可以通过BroadcastReceiver也能调用abortBroadcast()方法截断广播。

  • 动态注册即在代码中定义并设置好一个IntentFilter对象,然后再需要注册的地方调用Context.registerReceiver()方法。调用Context.unregisterReceiver()方法取消注册。
   IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.receiver");
         receiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 Log.d("服务器","接受到了广播发送数据");
             }
         };
         registerReceiver(receiver,intentFilter);
  • 本地广播只能在应用内部进行传递,而且广播接收器也只能接受本应用内自身发出的广播,本地广播使用LocalBroadcastManager来对广播进行管理。
private LocalBroadcastManager localBroadcastManager;
 localBroadcastManager = LocalBroadcastManager.getInstance(this);
 localReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        localBroadcastManager.registerReceiver(localReceiver, filter);

动态注册广播不是常驻型广播,它跟随activity的生命周期,静态注册是常驻型,即使程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
同优先级的广播接收器,动态优先于静态。

  • 小结:
    在Android中如果要发送一个广播必须使用sendBroadcast向系统发送对其感兴趣的广播接收器中。
    使用广播必须要有一个intent对象必设置其action动作对象。
    如果在AndroidManifest.xml中注册,当应用程序关闭的时候,也会接收到广播。在应用程序中注册就不产生这种情况了。

ContentProvider

contentprovider是Android四大组件之一的内容提供器,主要作用就是将程序的内部的数据和外部进行共享,为数据提供外部访问接口。是Android中一种跨程序共享数据的重要组件。

  • android:exported=“true”,运行跨进程访问。
  • grantUriPermissions=“true”,允许临时权限访问
  <provider
            android:name=".gassend.util.MyFileProvider"
            android:authorities="com.xiaoduo.xiangkang.gas.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file1_paths" />
        </provider>

Android的事件分发机制学习

在手指触摸屏幕后产生的一系列事件中,典型的事件类型如下:

  • ACTION_DOWN 在屏幕按下时;
  • ACTION_MOVE在屏幕上滑动时;
  • ACTION_UO手指在屏幕抬起时;
  • ACTION_CANCEL 取消;

什么是MotionEvent

MotionEvent就是Android对上面触摸事件相关信息的封装,View的事件分发中的事件就是这个MotionEvent,当这个MotionEvent产生后,那么系统就会将这个MotionEvent传递给View的层级,MotionEvent在View的层级传递的过程就是事件分发。MotionEvent封装了事件类型和坐标两类信息。
事件类型可以通过motionEvent.getAction()方法获取,返回一个常量,对应一个事件类型,事件类型主要有以下四种:

//MotionEvent.java
public final class MotionEvent extends InputEvent implements Parcelable {
    //按下(down)
    public static final int ACTION_DOWN             = 0;
	//抬起(up)
    public static final int ACTION_UP               = 1;
    //移动(move)
    public static final int ACTION_MOVE             = 2;
    //取消(cancel)
    public static final int ACTION_CANCEL           = 3;
    //...
}

坐标也是通过MotionEvent获取,motionEvent.getRawX()和motionEvent.getRawY()可以获得以屏幕作为参考系的坐标值,motionEvent.getX()、motionEvent.getY() 可以获得以被触摸的 View 作为参考系的坐标值。

一个事件序列

从手指按下屏幕到抬起,在这个过程中所产生的一系列事件,就是一个事件序列,这个事件序列以down事件开始,中间含有数量不定的move事件,最终以up事件结束。所以可能会有下面两种事件序列:

  • ACTION_DOWN->ACTION_UP手指按下屏幕后又抬起
  • ACTION_DOWN->ACTION_MOVE->…ACTION_MOVE_ACTION_UP手指按下屏幕,滑动一会后抬起
    在Activity的实现中,它会把事件交给PhoneWindow来分发,然后PhoneWindow又会把这个事件分发给顶级View,然后顶级View就调用super.dispatchTouchEvent方法,把这个输入事件在View树中层层分发下去,直到找到合适的View来处理这个事件。

事件分发的三个重要方法:

  • dispatchTouchEvent(MotionEvent event)调度触摸事件
    用来进行事件的分发,如果事件能传递给当前View,那么此方法一定能被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法影响,表示是否消费当前事件。

  • onInterceptTouchEvent(MotionEvent ev)在拦截触摸事件上
    用来进行事件的拦截,在dispatchTouchEvent()方法中调用,如果当前View拦截了某个事件,那么在同个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件。

  • onTouchEvent(MotionEvent ev)
    用来处理点击事件,在dispatchTouchEvent()方法中进行调用。返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。

分发事件的组件

在这里插入图片描述
ViewGroup就比如LinearLayout或者RelativeLayout,View就比如Button等控件。
子View可以通过requestDsiallowInterceptTouchEvent(bollen)方法设置是否允许ViewGroup的拦截事件。

结论

下面给出事件传递机制的一些结论,参考自《Android开发艺术探索》,根据这些结论可以更好地理解整个传递机制。

  • 同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程产生的一系列事件。这个事件义down开始,中间含有数量不定的move事件,最终以up事件结束。
  • 某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent不会再被调用。 这条也很好理解,就是说当一个View决定拦截一个事件后,那么系统会把同一个事件序列的其他方法都直接交给他来处理,因此就不用再调用这个View的onInterceptTouchEvent去询问它是否要拦截了。
  • 正常情况下,一个事件序列只能被一个View拦截消耗。 因为一旦一个元素拦截了某此事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理。(通过其他手段可以,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理)
  • 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false)那么同一事件序列中的其他事件(ACTION_MOVE,ACTON_UP…)都不会再交给他处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent将会被调用。 即事件一旦交给一个View处理,那么它就必须消耗掉,否则同一个事件序列中剩下的事件就不再交给它来处理。
  • 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false)那么同一事件序列中的其他事件(ACTION_MOVE,ACTON_UP…)都不会再交给他处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent将会被调用。 即事件一旦交给一个View处理,那么它就必须消耗掉,否则同一个事件序列中剩下的事件就不再交给它来处理。
  • 如果View不消耗除ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前View可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。
  • ViewGroup默认不拦截任何事件。 Andorid源码中ViewGroup的onInterceptTouchEvent方法默认返回false。
  • View没有onInterceptTouchEvent方法, 一旦有点击事件传递给它,那么它的onTouchEvent方法就会被调用
  • View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时返回false)。
  • 事件传递过程是由外向内的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素分发过程,但是ACTION_DOWN事件除外。
  • 在ViewGroup中ACTION_DOWN 事件负责寻找 target,即寻找能够消费ACTION_DOWN事件的子View,如果找到,那么接下来同一事件序列内的所有事件都会交给这个子View处理,不再交给ViewGroup;如果没有找到,有两种情况:1、ViewGroup没有子View,2、子View处理了ACTION_DOWN事件,但是在dispatchTouchEvent()返回了false,那么接下来同一事件序列下的所有事件都是ViewGroup自己处理。

本篇博客部分内容参考登楼应登楼以及rain9155

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪の星空朝酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值