记录一次四月份的面经
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属性表示是否启用这个广播接收器。
③进程的分类和优先级(如何设置系统的优先级)
④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