1.使用sharedPreferences保存数据
private void putShareData() {
SharedPreferences sharedPreferences = getSharedPreferences("phoneNum", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("devicePhone", AppInfo.phoneNumber);
editor.putString("localPhone", localPhone);
editor.commit();
}
private void getSharedData() {
SharedPreferences sharePreference = getSharedPreferences("phoneNum", MODE_PRIVATE); //可以使用PreferenceManager.getDefaultSharedPreferences获取实例
AppInfo.phoneNumber = sharePreference.getString("devicePhone", ""); localPhone = sharePreference.getString("localPhone", "");
}
2.Android视频
- ijkplayer 是一个基于 ffplay 的轻量级 Android/iOS 视频播放器。实现了跨平台功能,API易于集成;编译配置可裁剪,方便控制安装包大小;支持硬件加速解码,更加省电;提供Android平台下应用弹幕集成的解决方案,此方案目前已用于美拍和斗鱼 APP。
- Vitamio 能够流畅播放720P甚至1080P高清MKV,FLV,MP4,MOV,TS,RMVB等常见格式的视频,还可以在 Android 与 iOS 上跨平台支持 MMS, RTSP, RTMP, HLS(m3u8) 等常见的多种视频流媒体协议,包括点播与直播。
3.自定义view
- 自定义view步骤
1.了解view的工作原理
2.编写继承view的基类
3.为自定义view类增加属性
4.绘制控件
5.响应用户消息
6.自定义回调函数 - 实现方式
- 继承已有的控件实现
- 组合已有的控件实现
- 完全自定义控件
- view绘制流程
- Constructors 构造函数内进行一些参数的初始化,比如自定义属性
- onMeasure 测量View及其子View的宽高属性,这里是属性,而不仅仅是宽高的值
- onLayout 确定View及其子View的布局位置,也就是View及其子View在父容器中的坐标位置
- onSizeChanged View的大小发生改变时,将调用此函数,一个View的大小在绘制过程中可能发生改变,比如父View
- onDraw View的内容绘制部分,系统会提供给我们一块画布
4.android:textAppearance
设置文字外观。如 “?android:attr/textAppearanceLargeInverse”这里引用的是系统自带的一个外观,?表示系统是否有这种外观,否则使用默认的外观。可设置的值如下:textAppearanceButton/textAppearanceInverse/textAppearanceLarge/textAppearanceLargeInverse/textAppearanceMedium/textAppearanceMediumInverse/textAppearanceSmall/textAppearanceSmallInverse
5.制造延迟效果
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mMainPresenter.loadData();
}
}, 2000);
6.android 状态栏颜色
windowBackground设为白色,colorPrimaryDark设为透明时,个别机型会出现状态栏白色,状态栏的字体颜色也默认是白色,所以造成无法分辨和查看!
解决办法:colorPrimaryDark设为不透明,在布局中设置背景颜色控制状态栏颜色
7.Java获取时间格式
public static String getCurrentTime(String pattern){
SimpleDateFormat sdf=new SimpleDateFormat(pattern);
return sdf.format(new Date());
}
/**
* HH:mm 15:44
* h:mm a 3:44 下午
* HH:mm z 15:44 CST
* HH:mm Z 15:44 +0800
* HH:mm zzzz 15:44 中国标准时间
* HH:mm:ss 15:44:40
* yyyy-MM-dd 2016-08-12
* yyyy-MM-dd HH:mm 2016-08-12 15:44
* yyyy-MM-dd HH:mm:ss 2016-08-12 15:44:40
* yyyy-MM-dd HH:mm:ss zzzz 2016-08-12 15:44:40 中国标准时间
* EEEE yyyy-MM-dd HH:mm:ss zzzz 星期五 2016-08-12 15:44:40 中国标准时间
* yyyy-MM-dd HH:mm:ss.SSSZ 2016-08-12 15:44:40.461+0800
* yyyy-MM-dd'T'HH:mm:ss.SSSZ 2016-08-12T15:44:40.461+0800
* yyyy.MM.dd G 'at' HH:mm:ss z 2016.08.12 公元 at 15:44:40 CST
* K:mm a 3:44 下午
* EEE, MMM d, ''yy 星期五, 八月 12, '16
* hh 'o''clock' a, zzzz 03 o'clock 下午, 中国标准时间
* yyyyy.MMMMM.dd GGG hh:mm aaa 02016.八月.12 公元 03:44 下午
* EEE, d MMM yyyy HH:mm:ss Z 星期五, 12 八月 2016 15:44:40 +0800
* yyMMddHHmmssZ 160812154440+0800
* yyyy-MM-dd'T'HH:mm:ss.SSSZ 2016-08-12T15:44:40.461+0800
* EEEE 'DATE('yyyy-MM-dd')' 'TIME('HH:mm:ss')' zzzz 星期五 DATE(2016-08-12) TIME(15:44:40) 中国标准时间
*/
7.程序的复杂度
- 时间复杂度是总运算次数表达式中,受n的变化影响最大的那一项(不含系数)
- 空间复杂度是一个算法运行过程中临时占用存储空间大小的度量
8. 关于compileSdkVersion, minSdkVersion 和 targetSdkVersion
- compileSdkVersion,只有在编译时使用的api版本,尽可能使用较新的版本,可以避免新弃用的api
- minSdkVersion决定了应用运行的最低要求,使用第三方库时,最低api要比第三方库的最低api大
- targetSdkVersion 使应用向前兼容,使用新添加的功能,尽可能使用最新的版本
- 理想情况下应该是这样的:
minSdkVersion (lowest possible) <= targetSdkVersion == compileSdkVersion (latest SDK)
9.Java设计模式
- 构造者模式(将一个复杂对象的构造跟表示分离,通过相同的构造过程,创建不同的表示,例如:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
) - 原型模式 (用原型实例指定创建对象的类型,通过对原型的拷贝创建新的对象。使用方法:实现Cloneable接口,重写clone()方法。注意:使用原型模式不会创建新的对象,仅仅拷贝,节省创建对象对资源的消耗,深拷贝(拷贝时,数组,对象,集合需要自己实现进行拷贝)浅拷贝(基本数据类型会自动拷贝))
- 简单工厂模式,工厂模式,抽象工厂模式 (简单工厂:可以理解为一个创建对象的工具类。工厂模式:定义一个创建对象的接口,由子类来决定实例化哪一个类,工厂方法把类的实例化推迟到子类。抽象工厂模式:包含多个工厂类)
- 策略模式(有一系列算法,将每一个算法封装起来,每个算法之间可以相互替换。策略模式与工厂模式实现相同)
- 状态模式 (在状态模式中,行为是由状态来决定的,不同的状态对应不同的行为。状态模式与工厂模式实现相同,表达的目的不一样,一个关注对象的创建,一个关注行为的封装。减少耦合,少写if else 语句)
- 责任链模式 (使多个对象都有机会处理请求,从而避免请求的发送者与接受者直接的耦合关系,将这些对象连成一条链,并延这条链传递请求,直到有对象处理请求为止。减少耦合,少写if else 语句)
- 解释器模式 (给定一个语言,定义它的语法,并定义它的解释器,这个解释器用于解析语言。)
- 命令模式 (把请求或操作封装为对象,允许使用不同的请求把客户端参数化,对请求排队或记录请求日志,可以提供命令的撤销和恢复功能。好处:降低耦合性,易扩展)
- 观察者模式 (多个观察者监听被观察者,被观察者状态发生改变时通知观察者。观察者模式可以用接口回调来实现)
- 备忘录模式 (在不破坏封闭的情况下,捕获一个对象的状态,在对象之外保存这个状态,以后就可以把对象恢复到这个状态。Android 中
Activity的onSaveInstanceState和onRestoreInstanceState
就使用了备忘录模式,用于保存和恢复) - 迭代器模式 (用一种方法顺序地访问聚合对象中的元素,而不暴露对该对象的内部表示。就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器)
- 模板方法模式 (定义一个算法框架,使一些方法延迟到子类中执行,使得子类不改变算法的结构即可重定义算法的某些特定功能。用于对多次使用的算法封装起来,实现算法不变的部分,将可变的部分留给子类去实现)
- 访问者模式 (封装一些作用于数据结构元素之上的一些方法,一旦这些方法需要改变时,接受这个操作的数据结构保持不变)
- 中介者模式 (用一个中介对象封装一系列对象的交互,使得各个对象不需要显式引用,可以松散解耦。MVP架构中P层就使用了中介者模式进行M,V层的分离)
- 外观模式 (为子系统中的一组方法提供了统一的访问接口,这个接口使得子系统更容易访问和使用)
- 代理模式 (通过代理对象访问目标对象。好处是:在访问目标对象的基础上,增加额外的功能,扩展额外的公告,符合软件设计的开闭原则。主要分为:静态代理,动态代理)
- 装饰者模式 (动态地为一个类添加功能。相对继承父类来扩展功能,装饰者模式能减少子类的创建,可以动态加入。装饰者模式与代理模式相比:装饰者模式侧重于对装饰的对象功能扩展,代理模式侧重于对访问对象施加控制)
- 组合模式 (将对象组合成树形结构,以表示 部分与整体 层次结构,使得用户对单个对象和整体对象的使用具有一致性。)
- 适配器模式 (把一个类接口转换成客户期待的另一个接口,使原本由于接口不相兼容不能工作的两个类可以一起工作。当想使用一个既有类,既有类不满足当前类的功能时,可以考虑用适配器模式)
- 享元模式 (一个系统中可能有多个相同的对象,只共享一份对象就可以了,不用实例化每个对象。String类就使用了享元模式)
- 桥接模式 (把抽象跟实现分开,使它们能够独立的变化。)
10.Android studio导入项目一直停留在Build界面
主要原因:当前要打开项目中的gradle版本 与 电脑环境中gradle版本不一致,或者本地找不到,然后远程下载不下来 导致一直停留在Build界面
解决办法:查看能打开的项目,进入 gradle/wrapper/gradle-wrapper.properties 复制文件最后一行到不能打开的项目中,保存即可打开。
11.频繁gc会导致界面卡顿,或者卡死
频繁gc的一个原因可能是:定时器轮询任务中不断创建对象
12.TextView文字超过显示范围时,自动滚动显示
有多个时要设置:
textView.setSelected(true);
13.使用弱引用来解决内存泄漏
- 内存泄漏:静态变量,缓存,生命期长的对象,引用了其他对象,导致被引用的对象长时间得不到GC释放,导致内存泄漏
- 弱引用:弱引用的对象会被GC,在需要的时候释放内存
- 举例(使用静态类使内部类跟外部类无关,但是操作外部类的方法不可能全部申明静态的,所以要弱引用外部对象)
private Handler mHandler = new MyHandler(this);
private static class MyHandler extends Handler{
private final WeakReference activityWeakReference;
public MyHandler(VoicePagingActivity voicePagingActivity) {
activityWeakReference = new WeakReference<>(voicePagingActivity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
VoicePagingActivity activity = activityWeakReference.get();
if (activity == null){
return;
}
activity.soundRecordView.setDecibel(s);
activity.mHandler.sendEmptyMessageDelayed(0,5);
}
}
14.RecyclerView内部BUG:引起IndexOutOfBoundsException异常
- 主要原因:数据集合不同步,在改变list时没有及时进行notifyDataChange
- 解决办法:1.防止不同步(注意集合引用) 2.重写RecyclerView的LinearLayoutManager捕获异常
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
15.捕获异常
- 当某个程序执行,调用时可能会产生各种各样的异常,若不对某个异常进行处理,最好用全局异常捕获,例如:
try {
isTrue = myApplication.getOneLineAudioPrx().StopProgramCurProgramByTerminal(vDstTerminalIDS);
} catch (Exception e) {
e.printStackTrace();
}
- 捕获到异常后不会退出当前的函数调用,会继续往下执行,必要的时候在catch块中进行return处理
16.对EditText做输入限制
public static void setEditTextInput(final EditText editText) {
InputFilter filter = new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
//输入字符数限制
try {
if ((editText.getText().toString() + source.toString()).getBytes("utf-8").length > 32) {
return "";
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//过滤空格跟换行符
if (source.equals(" ") || source.toString().contentEquals("\n")) {
Log.e("setEditTextInputSpace1", source.toString());
return "";
}
//过滤特殊字符
String speChat = "[`~!@#$%^&*()+=|{}':;',\\\\.<>/?~!@#¥%……&*()——+|{}【】‘;:”“'。,、?]";
//String speChat="\\\\";(过滤\)
Pattern pattern = Pattern.compile(speChat);
Matcher matcher = pattern.matcher(source.toString());
if (matcher.find()) return "";
else return null;
}
};
editText.setFilters(new InputFilter[]{filter});
}
17.大小端问题
- 大小端跟cup架构有关,跟语言也有关,一般arm,x86平台是小端,Java跟平台无关,默认是大端,网络字节序一般是大端传输
- 大端是高位数据存放高地址(后面),小端是低位数组放在前面(低地址)
- 判断是否大小端,可以将0x01右移八位是否等于1,等于就是小端
- 大小端是面向多字节数据,单字节,字符串一般不用考虑
18.RecyclerView item不复用
在200多个item时导致界面加载很慢,点击反馈也很慢,无论加载还是点击item都会调用item总数个onBindViewHolder().经过各种尝试(放入一个新project各种测试))发现导致的原因是:界面布局问题。RecyclerView外层有两个LinearLayout再外层是SwipeRefreshLayout,把SwipeRefreshLayout直接包裹RecyclerView问题解决
19.链式调用的写法
- 定义Class
public class SessionBuilder {
//最好定义单例模式
public final static SessionBuilder getInstance() {
if (sInstance == null) {
synchronized (SessionBuilder.class) {
if (sInstance == null) {
SessionBuilder.sInstance = new SessionBuilder();
}
}
}
return sInstance;
}
public SessionBuilder setCallback(Session.Callback callback) {
mCallback = callback;
return this;
}
public SessionBuilder setContext(Context context) {
mContext = context;
return this;
}
public Session build() {
session = new Session();
//session 初始化...
return session
}
- 调用
session = SessionBuilder.getInstance()
.setCallback(this)
.setContext(MyApplication.getContext().getApplicationContext())
.setDestinationPort(port)
.setAudioEncoder(SessionBuilder.AUDIO_AAC)
.setAudioQuality(new AudioQuality(48000, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_IN_STEREO, 32000))
.build();
20.TextView显示问题
- singleLine 是用来限制行数,maxLines 用来限制高度
- 在RecyclerView中使用TextView设置singline属性,同时操作多个item时容易造成TextView不显示的问题;用maxLines 替代,但是当text中数字,英文字符,特殊字符混合时会造成TextView不满一行时自动换行,显示不全;解决办法:自定义控件继承TextView,重写onDraw,使用drawText进行绘制。
21.AsyncTask 执行后没有立即执行doInBackground()方法
- 原因:Android3.0 之后AsyncTask 调用execute()方法,线程串行执行,只要前一个任务没执行完,当前提交的任务就会被阻塞
- 解决办法:需要并行执行时使用
AsyncTask 的executeOnExecutor()方法,参数可以是:AsyncTask.THREAD_POOL_EXECUTOR,或者定义自己的线程池:ExecutorService executorService = Executors.newFixedThreadPool(5);
22.在MAC 系统下安装FFMPEG同时安装FFPLAY
-
brew reinstall ffmpeg --with-sdl2
(此命令可以保证成功安装ffplay) - 上面安装失败可以先尝试
brew install ffmpeg
23.TCP & UDP
- TCP 面向流的通信,发送端可以任意send,接收端可以recv任意大小的数据
- UDP面向无连接,发送端发送过快,接收端会接收不过来,导致丢包的出现(优化办法:1.请求一个包发送一次;2.控制接收发送的频率,接收后最好不做耗时的操作以防影响下次接收)
24.Activity全屏弹出Dialog导致导航栏,状态栏显示
- 弹出Dialog时禁用获取焦点可以解决
- 在dialog.show()之前调用:
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); //将会造成底部的view仍然可点击
- 必要的时候再做一下theme设置:
- true
- @null
25.关于组播
- 要想能收到组播,或者抓包工具能抓到组播包,必须本机加入组播
- 用组播socket可以收到组播包,也能收到单播包
- 用组播socket可以发送组播包,也能发送单播包
26.关于IntentService
- 第一次startService()时执行onHandleIntent()方法,随后再次调用startService(),执行onHandleIntent()(调用onHandleIntent执行完的情况下)或者onStartCommand()(调用onHandleIntent没执行完的情况下)
- onHandleIntent()本身就是一个后台线程,此线程执行结束,IntentService自动停止释放
- 每次启动startService()都会加入队列,瞬加启动太多次得不到及时处理会造成多个任务阻塞(解决方法:可以创建自己的队列进行监测)
27.APP长连接在断网时处理
- 针对APP启动时创建的长连接套接字,运行中途断网重新联网后最好重新进行创建长连接,尤其是当长连接在一个服务,一个线程阻塞运行时,断网后已经异常退出
- APP启动时没连上网,中间连上网,同样要重启长连接
27.EditText限制输入类型为IP
android:inputType="number"
android:digits="0123456789."
28.对大数字格式化
//@param pattern可以为:",###"
public static String parseNumber(String pattern, BigDecimal bd) {
DecimalFormatSymbols unusualSymbols = new DecimalFormatSymbols(Locale.CHINA);
unusualSymbols.setGroupingSeparator(' ');
DecimalFormat decimalFormat = new DecimalFormat(pattern, unusualSymbols);
return decimalFormat.format(bd);
}
29.判断android系统是否root
/**
* 判断手机是否拥有Root权限。
* @return 有root权限返回true,否则返回false。
*/
public boolean isRoot() {
boolean bool = false;
try {
bool = new File(“/system/bin/su”).exists() || new File(“/system/xbin/su”).exists();
} catch (Exception e) {
e.printStackTrace();
}
return bool;
}
30.Android studio 关于Git使用
- 搭环境(git安装,IDE环境配置,针对Git,Github)
- 分享项目到Github,别人可以进行clone,若要push则项目所在的Github设置使用ssh(别人可以直接提交代码到分支而不用审核),同时别人要把本地电脑的ssh key 加入到项目所在的账号
- commit push pull (commit只会提交到本地,push才能推到分支,pull拉取远程有更新的分支)
- 分支Branches (master作为主分支,有新的稳定版本发布时才从别的分支合并到主分支,通常会有个开发分支dev,专门做开发提交使用)
- 平时开发通常设置本地分支,远程分支,为dev分支;当dev分支要合并到master分支时,把远程master分支检出到本地变为master分支;把远程分支dev合并到本地分支master,最后push即可
31.AsyncTask处理耗时任务简易写法
AsyncTask.execute(new Runnable() {
@Override
public void run() {
//do time-consuming task
}
});
31.关于Android硬件加速
- 硬件加速一帧的绘制由主线程,绘制线程一起配合执行;软件加速只由主线程完成,所有的渲染都在主线程完成
- 硬件加速最终绘制交给GPU完成,软件加速最终绘制由CPU完成
32.Timer弊端
- 向前修改系统时间导致Timer来不了,向后修改系统时间导致Timer来的特别慢,主要原因Timer的机制问题导致,Timer以系统时间System.currentTimeMillis()为基准,修改了系统时间System.currentTimeMillis()也会更改,推荐使用:ScheduledExecutorService
- 第一个任务没执行完时会一直阻塞后面的任务,原因是:Timer是一种单线程调度的任务队列模型
33.字符串资源里变量替换
- 字符串定义:
第%1$s页 - 代码中获取:
String page = getString(R.string.page,"345");
34.关于Android系统
- 微内核:在核心的基础功能:内存管理,进程管理等功能上加入其他功能模块,这些模块可以是单独进程,不影响系统的核心功能;微内核的系统有鸿蒙OS;使用微内核的好处是:当一个功能模块出问题时不影响系统的整体运行,弊端是IPC通信开销没有进程内函数的直接调用开销高
- 宏内核:与微内核相反,把所有的东西都塞到内核里,共享同一块内存空间;linux内核用的就是宏内核
- 混合内核:揉和了两种内核的特性
- Android framework层中各个功能都是一个独立的进程,功能模块之间通过IPC通信,从这个角度看:Android系统是微内核
- Android系统发布更新过程:AOSP ==> 芯片厂商(兼容性适配,加入feature)==> 手机厂商(兼容性适配,加入feature)==> android手机
- Android的成功源于在framework层封装提供了大量常用功能,又把java作为APP编写语言吸引了大量Java开发人员进行开发,同时使用linux kernal使得大量硬件厂商几乎0成本的驱动开发适配
35.SparseArray源码解析
- SparseArray相对HashMap存储效率高,但SparseArray使用二分法查找键,导致存储大量数据时查找效率较低
- 内部使用两个一维数组保存键和值,通过键值映射进行扩容和缩容
36. ViewStub
- ViewStub 是一个看不见,没有大小,不占布局位置的View,用来懒加载布局。当ViewStub显示可见或inflate() 时就会加载布局并且替换ViewStub
37.Native代码中常见的:RefBase,sp,wp
- RefBase在Android的C++部分,是所有类的基类
- sp 强指针(strong pointer)
- wp弱指针(Weak Pointer)引用对象不需要手动释放
38.关于Serializable,Parcelable
- 都是用来进行序列化的接口,序列化:把对象转换为byte[],反序列化:把byte[]转换为对象;序列化的目的:1.进程间传递对象时,不能使用引用或指针传递对象,因为进程有自己的独立内存空间,同一个地址在不同的进程空间指向的内容不同(Activity之间传递对象需要实现序列化的接口,主要为了更好的兼容适配跨进程传递) 2.方便把对象中的字段,方法传递的同时,也方便保存到本地 3.网络传输
- Serializable属于java api 中提供的接口,效率较低,不适合内存间数据传递,但适合序列化到本地
- Parcelable属于Android api中提供的接口,效率较高,适合内存间数据传递,但不适合序列化到本地