四月份面经实录

记录一次四月份的面经

1、xx科技公司(智能产品方案公司)

主要需求技能:基于蓝牙、WiFi、2G、3G、4G、GPRS的开发。由于本人感兴趣开发的一款基于esp8266wifi模块连接数显数据收发的智能家居app吸引了面试官的眼球,后面的面试和问题基本上围绕着这一款app的制作来展开面试。

①首页的天气显示:天气显示使用什么网络请求框架来获取数据?

OkHttp网络请求框post和风天气的免费数据接口获取最近三天的网络数据详情,通过Gson解析返回的Json数组,拿到相对应的温湿度,风力等级,穿衣指数等数据然后回到主线程做UI更新。

②app和wifi模块主要是通过什么通信的?

通过Socket连接,wifi模块作为简单的服务器热点,实现手机与模块进行简单的数据交互。

③光传感器、温度传感器、湿度传感器、窗帘打开程度、模式选择等还有待商榷。


2、xx科技公司

①Android四大组件

Activity、Service、ContentProvider、BroadcastReceiver

②广播的注册方式和主要区别(引发了一个讨论,应用是否自启)

广播的注册方式有静态注册和动态注册两种注册方式。Android 8.0不允许后台应用开启后台服务,如有需要的话,可以判断系统等级后强制打开。

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
            context.startForegroundService(heartService);
        } else {
            context.startService(heartService);
        }

在郭霖老师的《第一行代码》中针对静态注册的描述是这样的:标题为:静态注册实现开机启动

    动态注册的广播接收器可以自由的控制注册与注销,在灵活性方面有很大的优势,但它也存在一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。那么有没有办法可以让程序在未启动的情况下就能接收到广播呢?这就需要使用到静态注册的方式了。静态注册广播接收器是在Mainfest文件中添加对应的Receiver标签,其中Exported属性表示是否允许这个广播接收器接收本程序以外的广播,Enabled属性表示是否启用这个广播接收器。


③进程的分类和优先级(如何设置系统的优先级)

1、foreground process
    正处于activity resume状态
    正处于bound服务交互的状态
    正处于服务在前台运行的状态(StartForeGround()被调用)
    Service生命周期正在被执行(onCreate(),onStart(),onDestroy())
    BroadcastReceiver正在执行onReceive()方法
    杀死foreground需要用户响应,因为这个安全优先级是最高的
    是用户操作所必须的,任一时间下,仅有少数进程会处于前台,仅当内存实在无法供给它们维持同时运行时才会被杀死。一般来说,在这种情况下,设备依然处于使用虚拟内存的状态,必须要杀死一些前台进程以用户界面保持响应。
•Android会依据进程中当前活跃组件的重要程度来尽可能高的估量一个进程的级别。比如说,如果一个进程中同时有一个服务和一个可视的activity,则进程会被判定为可视进程,而不是服务进程。
2、visible process
    activity不在前端显示,但也没有完全隐藏,能够看得见,比如弹出一个对话框
    一个bound到visible或者foreground的activity的service
    没有前台组件,但仍可被用户在屏幕上所见。当满足如下任一条件时,进程被认为是可视的:
• 它包含着一个不在前台,但仍然为用户可见的activity(它的onPause()方法被调用)。这种情况可能出现在以下情况:比如说,前台activity是一个对话框,而之前的    activity位于其下并可以看到。
• 它包含了一个绑定至一个可视的activity的服务。
可视进程依然被视为是很重要的,非到不杀死它们便无法维持前台进程运行时,才会被杀死。
3、Service process
    正在运行的,不在上述两种状态的service
是由 startService() 方法启动的服务,它不会变成上述两类。尽管服务进程不会直接为用户所见,但它们一般都在做着用户所关心的事情(比如在后台播放mp3或者从网上下载东 西)。所以系统会尽量维持它们的运行,除非系统内存不足以维持前台进程和可视进程的运行需要。
4、background process
    不可见状态的activity进程,onstop被调用
    包含目前不为用户所见的activity(Activity对象的 onStop() 方法已被调用)。这些进程与用户体验没有直接的联系,可以在任意时间被杀死以回收内存供前台进程、可视进程以及服务进程使用。一般来说,会有很多背景进程 运行,所以它们一般存放于一个LRU(最后使用)列表中以确保最后被用户使用的activity最后被杀死。如果一个activity正确的实现了生命周 期方法,并捕获了正确的状态,则杀死它的进程对用户体验不会有任何不良影响。
5、empty process
    没有运行任何component的进程,保留这个进程主要是为了缓存的需要
    不包含任何活动应用程序组件。这种进程存在的唯一原因是做为缓存以改善组件再次于其中运行时的启动时间。系统经常会杀死这种进程以保持进程缓存和系统内核缓存之间的平衡。
此外,一个进程的级别可能会由于其它进程依赖于它而升高。一个为其它进程提供 服务的进程级别永远高于使用它服务的进程。比如说,如果A进程中的内容提供者为进程B中的客户端提供服务,或进程A中的服务为进程B中的组件所绑定,则A 进程最低也会被视为与进程B拥有同样的重要性。

④http请求方式,post和get的区别

8中Http请求方式:

   OPTIONS 返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。

 HEAD 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。
 GET 向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在web app.中。其中一个原因是GET可能会被网络蜘蛛等随意访问。
 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
 PUT 向指定资源位置上传其最新内容。
 DELETE 请求服务器删除Request-URI所标识的资源。
 TRACE 返回显服务器收到的请求,主要用于测试或诊断。

 CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。


get和post的区别:

主要针对安全性来说,post参数隐藏,安全系数高,get会保存到浏览器,安全系数较低。


⑤图片的三级缓存技术,如何处理不必要的缓存,在什么地方处理(无框架使用时的原理机制)

图片的三级缓存技术分别是:内存缓存、本地缓存、网络缓存

下面是缓存的流程图:    



package com.jsako.showprodinfodemo;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.widget.ImageView;

/**
 * 图片加载类
 * 
 * @author Administrator
 * 
 */
public class ImageLoader {
    private Context context;
    private int loadingImage;
    private int errorImage;
    private LruCache<String, Bitmap> mapCache;
    public ImageLoader(Context context, int loadingImage, int errorImage) {
        this.context = context;
        this.loadingImage = loadingImage;
        this.errorImage = errorImage;
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int mCacheSize = maxMemory / 8;
        mapCache = new LruCache<String, Bitmap>(mCacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }
        };
    }

    public void getAndSetImage(String imagePath, ImageView iv_image) {
        //保存当前的ImageView对应的imagepath
        iv_image.setTag(imagePath);
        iv_image.setImageResource(loadingImage);
        /*
         * 从第一级缓存中找对应imagePath的图片 如果第一级缓存有对应图片,显示! 如果第一级缓存没有图片,从第二级缓存中找
         */
        Bitmap bitmap = getImageByFirstCache(imagePath);
        if (bitmap != null) {
            iv_image.setImageBitmap(bitmap);
            System.out.println("从一级缓存中找到");
            return;
        }
        /*
         * 从第二级缓存中找对应的图片 如果有,则缓存到第一缓存中 如果没有,则从第三集缓存中找
         */
        bitmap = getImageBySecondCache(imagePath);
        if (bitmap != null) {
            iv_image.setImageBitmap(bitmap);
            cacheInFirst(imagePath, bitmap);
            System.out.println("从二级缓存中找到");
            return;
        }

        /*
         * 从第三级缓存中找对应的图片 如果有,则缓存到第一、二缓存中 如果没有,则显示错误的图片
         */
        loadImageByThridCache(imagePath, iv_image);
    }

    /**
     * 将图片缓存到一级缓存
     * 
     * @param imagePath
     *            图片的url
     * @param bitmap
     */
    private void cacheInFirst(String imagePath, Bitmap bitmap) {

        mapCache.put(imagePath, bitmap);
    }

    /**
     * 从三级缓存中寻找图片
     * 
     * @param imagePath
     *            图片的url
     * @param
     * @return
     */
    private void loadImageByThridCache(final String imagePath,
            final ImageView iv_image) {
        new AsyncTask<String, Void, Bitmap>() {
            /**
             * 开启异步任务前调用
             */
            @Override
            protected void onPreExecute() {

            }

            /**
             * 异步任务完成后调用
             */
            @Override
            protected void onPostExecute(Bitmap result) {
                String nowImagePath=(String) iv_image.getTag();
                if(!nowImagePath.equals(imagePath)){
                    //如果当前请求的图片路径和需要显示的图片路径不一致的话,就不显示图片
                    System.out.println("不显示图片了");
                    return;
                }
                
                if (result != null) {
                    iv_image.setImageBitmap(result);
                } else {
                    iv_image.setImageResource(errorImage);
                }
            }

            /**
             * 后台进行异步任务
             */
            @Override
            protected Bitmap doInBackground(String... params) {
                String nowImagePath=(String) iv_image.getTag();
                if(!nowImagePath.equals(params[0])){
                    //如果当前请求的图片路径和需要显示的图片路径不一致的话,就不进行网络请求了
                    System.out.println("不进行网络请求了");
                    return null;
                }
                String url = params[0];
                HttpURLConnection conn = null;
                try {
                    URL mUrl = new URL(url);
                    conn = (HttpURLConnection) mUrl.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setReadTimeout(6000);
                    conn.setConnectTimeout(6000);
                    conn.setDoInput(true);
                    conn.connect();

                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        // 在分线程中缓存图片到一级和二级缓存
                        cacheInFirst(url, bitmap);
                        String imageName = url
                                .substring(url.lastIndexOf("/") + 1);
                        String fileName = context.getExternalFilesDir(null)
                                .getAbsolutePath() + "/" + imageName;
                        bitmap.compress(CompressFormat.JPEG, 50,
                                new FileOutputStream(fileName));
                        return bitmap;
                    } else {
                        return null;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    if (conn != null) {
                        conn.disconnect();
                    }
                }
            }
        }.execute(imagePath);
    }

    /**
     * 从二级缓存中寻找图片
     * 
     * @param imagePath
     *            图片的url
     * @return
     */
    private Bitmap getImageBySecondCache(String imagePath) {
        String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
        String fileName = context.getExternalFilesDir(null).getAbsolutePath()
                + "/" + imageName;
        return BitmapFactory.decodeFile(fileName);
    }

    /**
     * 从一级缓存中寻找图片
     * 
     * @param imagePath
     *            图片的url
     * @return
     */
    private Bitmap getImageByFirstCache(String imagePath) {
        return mapCache.get(imagePath);
    }

}

⑥activity的启动模式

standard、singleTop、singleTask、singleInstance 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值