记录一次四月份的面经
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