目录
2.在很多的工具类或者获取系统的一些组件时总是要用到context的
4.升级到AndroidStudio4.0后,调试程序有时安装成功并启动了,但安装完成后想再次安装时就不会启动程序了(好像是安装成功了但没有启动)。
5.使用ScreenMatch适配,最近适配公司买的主板的机器,按照默认的去适配没有找到1080P的适配。所以只能看看有没有其他的方法了。
6.最近公司用到一个美颜的SDK,主要接触到OPENEGL不怎么多。但公司的需求是要一边预览实况,另一边在拍照后要在预览的情况下在后台对图片进行美颜。
7.使用DialogFragment显示时,虚拟按钮显示的情况。查找资料都说在Dialog在显示前隐藏虚拟按钮的。
11.Gradle's dependency cache may be corrupt
12.使用Glide加载本地图片的问题:(问题出现在android7.1系统的设备)
15.TextToSpeech根据文字读出声音(好像之前的系统都不支持中文的,我现在用的Android10的系统是可以读出中文的)
17.将As4.0版本升级到AS4.1.3版本提示JRE没有此方法。
18.使用SpannableStringBuilder对TextView显示的字符串设置不同的颜色。
20.使用TextView显示扑克牌的Unicode花色时,花色会按黑红黑红的颜色显示了。
21.AndroidStudio依赖的包文件报红(程序可以运行,但就是想通过点击代码到这个类就不行)
22.将Assets里的数据库转存到程序本地数据库文件夹,再将其数据转移到相应的表。(LitePal数据库)
25.AndroidStudio中的Gradle打包中没有相关的Tasks任务。
29.使用Socket在局域网WIFI情况TCP传输h264数据引出的BUG。
1.AS的个人设置
Appearance 设置主题Darcula(黑色)
Editor->General->Code Completion->选择none模糊匹配。(AndroidStudio4.0.1版本直接不点击match case 即为模糊匹配)
Editor->Color Scheme->Console Font 设置控制台的大小字体
Editor->Color Scheme->Color Scheme Font 设置代码的大小字体
点击打开项目文件时,左侧树形会跳转到相应的位置:在项目右侧的设置图标中勾选“Always Select Opened File”即可。
keymap设置快捷键
公司使用SVN来保存和管理代码:但忽悠文件又没写好,如.gradle和.idea里的文件会经常变动的。所以在AS中主动设置忽略:Editor--》File Types -->Ignored Files And Folders里添加即可。
2.在很多的工具类或者获取系统的一些组件时总是要用到context的
考虑对象在内存的使用问题,一般来说都会首先使用getApplicationContext()或者getContext()。
但如果可能的话最后看看后面是否会调用Context去跳转页面或者其他有关UI的(如Dialog
的使用之类的),最好在Activity中传的参数是:*Activity.this。
因为在项目中遇到传getApplicationContext()或者getContext()这两个参数的时候,部分机型也是没问题,但用户不可能会使用指点的机型的。所以就会出现问题了。(遇见的情况就是使用有盾云慧眼的人脸识别时,封装成一个工具来使用时,传的参数不是Activity对象时,有些机型会出问题会不能跳转到人脸识别的页面。)
集合元素交换位置
// // 要遍历的次数
// for (int i = 0; i < bluetoothRssis.size() - 1; i++) {
// // 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
// for (int j = bluetoothRssis.size() - 1; j > i; j--) {
// // 比较相邻的元素,如果前面的数小于后面的数,则交换
// if (bluetoothRssis.get(j - 1).getRssi() < bluetoothRssis.get(j).getRssi()) {
// Collections.swap(bluetoothRssis, (j - 1), j);
// }
// }
// }
3.利用Collections.sort排序集合
return 0不处理,return -1表示向前移一位,return表示向后移一位。
4.升级到AndroidStudio4.0后,调试程序有时安装成功并启动了,但安装完成后想再次安装时就不会启动程序了(好像是安装成功了但没有启动)。
上网找原因以为是Install Run的原因。
但在AndroidStudio3.5之后Install Run就已经取消了。
网上也有说是那个类似于热修复的问题安装(apply Changes)。
最终在另一个博客的评论找到答案,记录一下:3.5版以后的应该都可以在Editor-->Inspections里面可以找到,不过名字变成了Thread issues了,点击打钩,后面再搞搞问题还是没解决这好像与手机系统有点问题的。在模拟器和小米手机中都没出现这种现象,在华为手机上出现此问题的。
5.使用ScreenMatch适配,最近适配公司买的主板的机器,按照默认的去适配没有找到1080P的适配。所以只能看看有没有其他的方法了。
最后看到Android屏幕适配(一):ScreenMatch官方屏幕适配方案_feng海涛-CSDN博客
里的方法就行了。
DisplayMetrics d = getResources().getDisplayMetrics();
Log.d(Const.TAG, "数据dpi d.density:" + d.density);
Log.d(Const.TAG, "数据dpi densityDpi:" + d.densityDpi);
Log.d(Const.TAG, "数据dpi heightPixels:" + d.heightPixels);
Log.d(Const.TAG, "数据dpi scaledDensity:" + d.scaledDensity);
Log.d(Const.TAG, "数据dpi widthPixels:" + d.widthPixels);//我在项目中看到用这个分辨率的,所以加上1080的
Log.d(Const.TAG, "数据dpi xdpi:" + d.xdpi);
Log.d(Const.TAG, "数据dpi ydpi:" + d.ydpi);
Configuration config = getResources().getConfiguration();
int smallestScreenWidth = config.smallestScreenWidthDp;//最小宽度DP值(表明使用哪个SW)
Log.d("Const.TAG", "数据dpi smallestScreenWidth:" + smallestScreenWidth);
按提示加上或者去除不需要的分辨率即可。
StringBuilder swdef = new StringBuilder(); // PrintWriter out; try { swdef.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<resources>"); double value; for (int i = 0; i < 500; i++) { //这里控制对应转换的值,如果是标准尺寸就一对一转换 value = (i + 1) * 1; value = Math.round(value * 100) / 100; swdef.append("<dimen name=\"dp" + (i + 1) + "\">" + value + "dp</dimen>\r\n"); } swdef.append("</resources>"); //这里是文件名,注意修改 sw 后面的值,和转换值一一对应 String filedef = "./app/src/main/res/values-sw380dp/dimens.xml"; System.out.println(swdef.toString()); // out = new PrintWriter(new BufferedWriter(new FileWriter(filedef))); // // out.println(swdef.toString()); // // // out.close(); } catch (Exception e) { e.printStackTrace(); } finally { }
6.最近公司用到一个美颜的SDK,主要接触到OPENEGL不怎么多。但公司的需求是要一边预览实况,另一边在拍照后要在预览的情况下在后台对图片进行美颜。
但SDK里只有单独对图片,或者单独实况预览的。
(1)刚开始的思路是建立一个glsurfaceview在前台(实况控件在这个控件之下,但这个控件只有1像素)。
但因为在渲染第二张图片时,他不会再重新走onSurfaceCreated(图片在这个方法传入的,暂时没想到有什么方法就用第二种了)。
(2)所以想着用代码加控件的,但因为公司安装程序的机器是安卓7.0系统的。网上都说用view.bringToFront()是放在前台的,手机上测试也是这样。但放到机器上安装时,就死活看不到。
只能再找找办法了。后来找到说是什么glsurfaceview的遮盖之类的,要用到glsurfaceview.setZOrderOnTop(true)才行。
最后图片是美颜成功了,但这个时间不好把控。只能SDK的技术支持了。
(3)新开一个渲染线程,再在线程中轮循一下(SDK的画帧的方法)。
除了自定义一个渲染线程外,其实也可以用新建的glsurfaceview中的渲染线程去处理的。这样的情况就不用怎么关心退出页面时的内存泄漏问题(因为这个控件我只是单纯使用他的渲染线程)。
不好的地方可能得放在前台去处理。
下面是自定义的渲染线程:
public class CustomRenderThread extends HandlerThread {
private static final String TAG = "GLThread";
private Handler mHandler;
private EGLConfig eglConfig = null;
private EGLDisplay eglDisplay = EGL14.EGL_NO_DISPLAY;
private EGLContext eglContext = EGL14.EGL_NO_CONTEXT;
private EGLSurface eglSurface = null;
public CustomRenderThread() {
super("GLRenderer");
}
/**
* 创建OpenGL环境
*/
private void createGL(){
// 获取显示设备(默认的显示设备)
eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
// 初始化
int []version = new int[2];
if (!EGL14.eglInitialize(eglDisplay, version,0,version,1)) {
throw new RuntimeException("EGL error "+ EGL14.eglGetError());
}
// 获取FrameBuffer格式和能力
int []configAttribs = {
EGL14.EGL_BUFFER_SIZE, 32,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
EGL14.EGL_NONE
};
int []numConfigs = new int[1];
EGLConfig[]configs = new EGLConfig[1];
if (!EGL14.eglChooseConfig(eglDisplay, configAttribs,0, configs, 0,configs.length, numConfigs,0)) {
throw new RuntimeException("EGL error "+ EGL14.eglGetError());
}
eglConfig = configs[0];
// 创建OpenGL上下文
int []contextAttribs = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
eglContext = EGL14.eglCreateContext(eglDisplay, eglConfig, EGL14.EGL_NO_CONTEXT, contextAttribs,0);
if(eglContext== EGL14.EGL_NO_CONTEXT) {
throw new RuntimeException("EGL error "+ EGL14.eglGetError());
}
eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, configAttribs, 0);
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
}
/**
* 销毁OpenGL环境
*/
private void destroyGL(){
EGL14.eglDestroySurface(eglDisplay, eglSurface);
EGL14.eglDestroyContext(eglDisplay, eglContext);
EGL14.eglReleaseThread();
eglContext = EGL14.EGL_NO_CONTEXT;
eglDisplay = EGL14.EGL_NO_DISPLAY;
eglSurface=null;
eglConfig=null;
}
@Override
public synchronized void start() {
super.start();
mHandler = new Handler(getLooper());
mHandler.post(new Runnable() {
@Override
public void run() {
createGL();
}
});
}
public void release(){
if (null == mHandler)return;
mHandler.post(new Runnable() {
@Override
public void run() {
destroyGL();
quit();
mHandler.removeCallbacksAndMessages(null);
}
});
}
public void post(Runnable runnable){
if (null == mHandler)return;
mHandler.post(runnable);
}
}
建立对象,然后start,在需要使用的地方使用post方法即可。退出页面就执行release()。
7.使用DialogFragment显示时,虚拟按钮显示的情况。查找资料都说在Dialog在显示前隐藏虚拟按钮的。
(重点:原本隐藏的虚拟按钮为什么会显示呢,查找资料说是window的焦点从Activity变成Dialog了)
但我直接去隐藏虚拟按钮时,虚拟按钮的位置直接变黑了。
最后参考了:Android dialog 弹出时,保持 隐藏 navigation bar(虚拟栏)_大于弱智的博客-CSDN博客
在Dialog显示前设置
Window().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
显示后先隐藏虚拟按钮,再clearFlags。
因为DialogFragment相当于就是一个Fragment上加载一个Dialog似的。
所以去DialogFragment中查看一下源码。查看Dialog显示是在OnStart的,所以:
@Override
public void onStart() {
if (getDialog()!=null){
getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}
super.onStart();
}
在显示后(可以监听OnShowListener获取显示的监听)
@Override
public void onShow(DialogInterface dialog) {
DialogUtils.hideNavigationBar(getDialog().getWindow());
getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}
/**
* 隐藏虚拟栏 ,显示的时候再隐藏掉
* @param window
*/
static public void hideNavigationBar(Window window) {
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
window.getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
//布局位于状态栏下方
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
//全屏
View.SYSTEM_UI_FLAG_FULLSCREEN |
//隐藏导航栏
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
if (Build.VERSION.SDK_INT >= 19) {
uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
} else {
uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
}
window.getDecorView().setSystemUiVisibility(uiOptions);
}
});
}
8.接口里的方法可选择重写或者不重写:
因为default是在java8中引入的关键字,也可称为Virtual
所以在接口方法加上这个关键词后,可以选择是否重写这个方法的。
public interface Test {
void aa();
default void bb(){
Log.e("TAG", "bb: " );
}
}
当普通类以Test为接口时,aa()为必须重写的方法。bb为可以选择重写也可以不重写的。
9.定位ANR
使用adb pull data/anr/traces.txt显示日志
搜索group="main"确定为主线程出问题的点(好像是搜索自己的包名和group="main"定位的,我遇到的ANR不知道是不是系统差造成的)。
10.调试视频播放时,在网上找到的两个视频播放链接:
http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4(1分钟)
http://vfx.mtime.cn/Video/2019/03/09/mp4/190309153658147087.mp4(1分34秒)
11.Gradle's dependency cache may be corrupt
遇到这个错误是打开旧项目,下载旧项目的gradle版本出问题了。(例如打开项目,然后下载。这个gradle只下载了一部分会提示解压失败的)
解决:1.在网上找这个项目相对应的gradle版本下载放到相应的位置。
2.将项目的gradle更改为最新的gradle。
3.最后都不想的话就去C:\Users\Administrator\.gradle\wrapper\dists自己电脑上的.gradle路径下删除gradle-4.6-all,再重新加载项目让AS慢慢下载了。
12.使用Glide加载本地图片的问题:(问题出现在android7.1系统的设备)
刚刚开始为了方便就直接
Glide.with(mContext).load(path).into(imageView);
这样的情况加载网络图片,看到完全没问题的,而在下载完图片到本地就加载本地的图片。
正常情况下在测试时都没看到出现问题,但同一个图片,有些设备在显示本地图片时可能会出现其他情况。
我这里当前的情况是下载的图片是完整的。但在显示的时候只显示了图片的四分之一,剩余部分就用灰色去填充了。
解决方法:
Glide.with(mContext).load(new File(path)).into(imageView);当要显示本地图片就得new file才能有点点保证没出现这个错。
13.获取手机卡序列号(iccid1):
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
SubscriptionManager sm = SubscriptionManager.from(context);
@SuppressLint("MissingPermission") List sis = sm.getActiveSubscriptionInfoList();
SubscriptionInfo si = sis.get(0); String iccid = si.getIccId();
}else {
//获取sim卡信息
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
// @SuppressLint("MissingPermission") String deviceid = tm.getDeviceId();
@SuppressLint("MissingPermission") String tel = tm.getLine1Number();
@SuppressLint("MissingPermission") String iccid1 = tm.getSimSerialNumber();
@SuppressLint("MissingPermission") String imsi = tm.getSubscriberId();
int simState = tm.getSimState(); }
14.关于DB模式
createDeviceProtectedStorageContext()方法
Context deviceContext = context.createDeviceProtectedStorageContext();
因为Android7..0之后就有这个DB模式问题,最近搞华为SDK要用到这个的。
正常模式是可以访问到data/data/包名/share_pre的SharePerfener文件。但当手机重启且手机设置密码(没开锁)的情况,就访问不到这个文件夹,这样就会使程序无法访问相应的SP文件。
在相应的四大组件中加入
android:directBootAware="true"且监听开机广播。
这样的情况可以使程序可以在开机后运行
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
Context deviceContext = mContext.createDeviceProtectedStorageContext();
deviceContext.moveSharedPreferencesFrom(mContext, spName);
sp = PreferenceManager.getDefaultSharedPreferences(deviceContext);
}else {
sp = mContext.getSharedPreferences(spName, mode);
}
(只是针对自己的项目,做的笔记)
15.TextToSpeech根据文字读出声音(好像之前的系统都不支持中文的,我现在用的Android10的系统是可以读出中文的)
lateinit var textSpeect: TextToSpeech
textSpeect= TextToSpeech(this, TextToSpeech.OnInitListener {
Log.e("TAG", "onCreate:$it " )
if (it==TextToSpeech.SUCCESS){
// 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
textSpeect.setPitch(1f)
// 设置语速
textSpeect.setSpeechRate(0.2f)
var supported=textSpeect.setLanguage(Locale.CHINA)
if ((supported!=TextToSpeech.LANG_AVAILABLE)&&(supported!=TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
Toast.makeText(applicationContext, "不支持当前语言!", Toast.LENGTH_SHORT).show();
}
}
})
textSpeect.speak("文字转语音 不支持当前语言",TextToSpeech.QUEUE_FLUSH,null)
读出声音(只有textSpeect初始化成功才可以读出这些文字的,比如在Android7中使用这代码,初始化是不成功的提示不支持当前语言的)
最后在OnStop中关闭
textSpeect.stop()
textSpeect.shutdown()
-》转载自:https://blog.csdn.net/qq_29449697/article/details/112466697
因为Android11 软件包可见性问题导致TextToSpeech初始化失败,只需要在AndroidManifest.xml文件中增加标签即可
转载自:TextToSpeech 因为Android11应用读取权限问题初始化失败_爱玩不变的博客-CSDN博客 《-
<queries> <intent> <action android:name="android.intent.action.TTS_SERVICE" /> </intent> </queries>
监听说的话是否完成:
textToSpeech.speak(lightBean?.title,TextToSpeech.QUEUE_FLUSH,null,"1")
textToSpeech.setOnUtteranceProgressListener(object : UtteranceProgressListener(){ override fun onDone(utteranceId: String?) { Log.e("TAG", "onDone: $utteranceId" ) when(utteranceId){ "1","2"->{ //todo 此处不能刷新UI,要更新UI得切换回UI线程 } } } override fun onError(utteranceId: String?) { Log.e("TAG", "onError: $utteranceId" ) } override fun onStart(utteranceId: String?) { Log.e("TAG", "onStart: $utteranceId" ) } })
speak时要加上utteranceId参数,若不加上则不会进入这个监听方法的。
如设置utteranceId为1,则在ondone中监听说完后的操作。
16.Kotlin实体类的构造方法
data class ResultLiveData(
val testResult: TestResult?,
val responseThrowable: ResponseThrowable?
){
constructor(testResult: TestResult) : this(testResult,null)
constructor(responseThrowable: ResponseThrowable) : this(null,responseThrowable)
}
放在类名之后的构造方法为主构造方法,之后定义的都是次构造方法。
如果要建次构造方法就得在之后加this :(name1,name2),调用主构造方法将其他的未在次构造方法的数据赋初始值。
17.将As4.0版本升级到AS4.1.3版本提示JRE没有此方法。
将C:\Users\ASUS\AppData\Roaming\Google里的AndroidStudio4.1删除即可,然后重新打开AS就行。
18.使用SpannableStringBuilder对TextView显示的字符串设置不同的颜色。
String timeStr=(timeCount-aLong)+"S";
binding.tvPrintPrintTimeText.setText(
setSpannableText(
getResources().getString(R.string.print_print_time_text,timeStr),
timeStr,
getResources().getColor(R.color._FF9600)));
/**
* 在Textiview中对特定字符串进行改变颜色处理
* @param str 要显示的字符串
* @param changColorStr 显示中要改变颜色的字符串
* @param colorId 显示的颜色ID
* @return
*/
private SpannableStringBuilder setSpannableText(String str,String changColorStr,int colorId){
SpannableStringBuilder style = new SpannableStringBuilder(str);
int start=str.indexOf(changColorStr);
int end=start+changColorStr.length();
// style.setSpan(new ForegroundColorSpan(Color.RED), 0, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//设置颜色
style.setSpan(new ForegroundColorSpan(colorId), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//设置大小
// style.setSpan(new RelativeSizeSpan(0.75f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return style;
}
当注释的两行也开启的效果图为:
RelativeSizeSpan设置的字体大小的比例(相对于TextView设置了TextSize的,比如我这个Textview设置了16sp,当设置为0.75f的比例时就相当将字体设置为12sp了)
19.Kotiln中使用到懒加载后,判断是否已初始化。
this::adapter.isInitialized
一定要加上::不然无法写出此方法。(在Kt中使用Bind的方法使用Service是经常使用到,所以记录下。)
20.使用TextView显示扑克牌的Unicode花色时,花色会按黑红黑红的颜色显示了。
符号 | UNICODE | 符号 | UNICODE | ||||
---|---|---|---|---|---|---|---|
HTML | JS | CSS | HTML | JS | CSS | ||
♠ | ♠ | \u2660 | \2660 | ♣ | ♣ | \u2663 | \2663 |
♥ | ♥ | \u2665 | \2665 | ♦ | ♦ | \u2666 | \2666 |
♤ | ♤ | \u2664 | \2664 | ♧ | ♧ | \u2667 | \2667 |
♡ | ♡ | \u2661 | \2661 | ♢ | ♢ | \u2662 | \2662 |
显示前面四个时系统自己改变了颜色了。后面四个就会根据TextView的颜色改变而改变。
后来看了如何在TextView中更改unicode字符的颜色? - VoidCC
这个帖子看到在\u2665后面加上 \ uFE0E就会相当于随TextView的颜色改变而改变了,当然他也给出了另一个解决方案就是改变字体,使用一种显示花色而不会改变颜色的字体(没找着具体字体包下载就不搞了)
21.AndroidStudio依赖的包文件报红(程序可以运行,但就是想通过点击代码到这个类就不行)
4.1以下版本就是删除C盘中的用户文件夹下的.AndroidStudio3.6(对应版本)里的除config文件夹外的文件。
4.1及以上版本就删除C:\Users\Administrator\AppData\Local\Google中对应版本的文件夹即可。
22.将Assets里的数据库转存到程序本地数据库文件夹,再将其数据转移到相应的表。(LitePal数据库)
File dbFile=new File("/data/data/com.bwzn.demo/databases/","ToolCar.db"); boolean saveFile=false; if (!dbFile.exists()){//复制数据 saveFile= FileUtils.copyAssetsFile(getApplicationContext(),dbFile.getName(),dbFile.getParent()); } if (dbFile.exists()){ LitePalDB litePalDB = new LitePalDB("ToolCar", 4); litePalDB.addClassName(ToolEt.class.getName()); LitePal.use(litePalDB); List<ToolEt> inventoryTool=LitePal.findAll(ToolEt.class); LogUtils.i(ToolEt.class.getName()+"===== "+inventoryTool.size()); if (inventoryTool.size()>0){//todo 数据库有相应的表数据就将其转移 SPUtils.save(ConstSpKeyData.FIRST_SAVE_DB,false); List<ConsumableDbBean> consumableBeanList=new ArrayList<>(); for (ToolEt toolEt: inventoryTool){ ConsumableDbBean bean=new ConsumableDbBean(); bean.setLocation(toolEt.getLocation()); bean.setName(toolEt.getDesp()); bean.setEpc(toolEt.getBarcode()); consumableBeanList.add(bean); } LitePal.useDefault(); LitePal.saveAll(consumableBeanList); } }
23.根据包名获取第三方应用的启动页。
(1)
Intent temp1= getActivity().getPackageManager().getLaunchIntentForPackage(packageName); 若没有启动页temp1为null的,不为null时可以打印temp1.getComponent()查看
(2)
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); List<ResolveInfo> mAllApps=getActivity().getPackageManager().queryIntentActivities(mainIntent, 0); //按包名排序 Collections.sort(mAllApps, new ResolveInfo.DisplayNameComparator(getActivity().getPackageManager())); //遍历 for(ResolveInfo res : mAllApps) { if (res.activityInfo.packageName.equals(packageName)){ LogUtils.e(" === "+res.activityInfo.name); } }
24混淆Library和jni
混淆Library参考Android多模块混淆、多module混淆、多library混淆的正确姿势_逆境中徘徊的博客-CSDN博客_android library 混淆
自己在此记录一下:(1)混淆规则与app包基本一样。只是相应的依赖库或者内容不一样而已。
(2)app包开启混淆,Library默认与app包一致(app包开启,Library也开启)
配置:
在Library中build.gradle添加
buildTypes { debug{ consumerProguardFiles "proguard-rules.pro" } release { consumerProguardFiles "proguard-rules.pro" } }
因为我当前的Library很多都可以混淆,所以只是对实体类不混淆就行了。
jni的混淆:(网上一个个找的,反正后面就没发生问题就没简化了)
-keepclassmembers class com.*.*.HelloJni{*;} -keep class com.*.*.HelloJni{*;} -keepnames class com.*.*.HelloJni{*;} -keepclassmembernames class com.*.*.HelloJni{*;} # 设置所有 native 方法不被混淆 -keepclasseswithmembernames class * { native <methods>; } #不混淆类 -keepclasseswithmembernames class com.*.*.HelloJni {*;} -keep class com.*.*.HelloJni { native <methods>; }
==================
HelloJni 里包含
System.loadLibrary这个方法和相应的 native方法。
注意:如果使用SharedPreferences保存数据的话,在getSharedPreferences(name,Context.MODE_PRIVATE)中的name如果使用类名称作为sp保存的文件命名。直接使用字符串来代码,不要用使用SpUtils.class.getSimpleName,因为在混淆后Sputils的名称不一定是这样的了,有可能是X,所以混淆后与混淆前读取的sp数据会不一样(读取的文件就不一样,所以会有问题)。
25.AndroidStudio中的Gradle打包中没有相关的Tasks任务。
打开File---》Experimental----》看Gradle 将“Do not build gradle task list during gradle sync”的勾
去除,然后在File中点击“Sync Project with Gradle Files”即可。
26.以某点为圆点旋转,得到新的坐标点位置。
Matrix matrix=new Matrix(); matrix.setRotate(90,getWidth() / 2.0f, getHeight() / 2.0f); float point[] = {cx, cy}; matrix.mapPoints(point);
其中这里是以控件中心旋转的,如果以原点旋转可以写0或者不写也行。
cx,cy为旋转前的坐标,旋转后的坐标为point[0]和point[1]
27.混淆处理过程中,不混淆aar第三方库。
(直接使用-keep class 包名就可以不混淆aar第三方库了,以下的提示“key pair generate err” 只是因为引入的库接入时要依赖一个加密的库,在做混淆处理时,没有加这个混淆规则所以才会提示这个错误。下面的只是自己混淆过程中多加码的东西,不删除是让自己记住这个错而已)
因为引用的第三方库还没完善(一直开发中),而公司又要版本升级而且还要混淆。但第三方库没有混淆规则,故网上查了一堆都说-keep class包名是可以。加入这个包名的混淆规则,打包然后分析安装包,看着确实有第三方库的内容。但因为这个库是有一个包名对应的appid的(个人理解当这个是密钥),在混淆做一些操作失败后的提示为:“key pair generate err”。
所以这样混淆还是不行,只能再找找了。
说使用-libraryjars的,使用了没有作用。
说使用
#-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,Annotation,EnclosingMethod,MethodParametersclass # #-keep class **.R$* {*;}
的也没有作用。
最后看一下别的第三方的规则了:
看见除了-keey class 包名外。还有一个-dontwarn 包名的(
不提示警告 dontwarn是一个和keep可以说是形影不离,尤其是处理引入的library时.
)
如腾讯文档库的:(类似这样添加就不混淆第三方库的)
-keep class com.tencent.smtt.**
-keep class com.tencent.smtt.**{*;}
-dontwarn com.tencent.smtt.**
-keep class com.tencent.tbs.**{*;}
28.使用Cursor扫描指定后缀名的文件。
参考文章链接:Android查询扫描SD卡里指定后缀名称的文件_黄油奥特曼的博客-CSDN博客
String[] queryFileTypes={"%.pdf","%.txt","%.doc","%.docx","%.xlsx","%.xls","%.ppt","%.pptx","%.epub"}; List<String> files = new ArrayList<>(); for (String queryfileType : queryFileTypes){ //每扫描一次得到相应后缀名的结果 files.addAll(queryFiles(context,queryfileType)) ; }
private List<String> queryFiles(Context context,String queryFileType){ List<String> files = new ArrayList<>(); String[] projection = new String[] { MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA, MediaStore.Files.FileColumns.SIZE }; //使用 MediaStore.Files.getContentUri("external")获取的URI同样是content://media/external/file Cursor cursor = context.getContentResolver().query( Uri.parse("content://media/external/file"), projection, MediaStore.Files.FileColumns.DATA + " like ?", new String[]{queryFileType}, null); if (cursor != null) { if (cursor.moveToFirst()) { int idindex = cursor.getColumnIndex(MediaStore.Files.FileColumns._ID); int dataindex = cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA); int sizeindex = cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE); do { String id = cursor.getString(idindex); String path = cursor.getString(dataindex); String size = cursor.getString(sizeindex); File file=new File(path); //因为根据参考的文章的评论和实际结果:被删除的也可能被扫描到,还有一个情况是 //如果文件夹的命名也是类似于文件的命名,这里同样会扫描到的,所以过滤不相关的得到想要的结果 if (file.exists() && file.isFile()){ files.add(path); } } while (cursor.moveToNext()); } cursor.close(); } return files; }
29.使用Socket在局域网WIFI情况TCP传输h264数据引出的BUG。
使用场景是线程采集到数据,然后进行H264编码,相应的sps和pps进行静态保存到同一个数组中。因为预定不是只有一个端接收的,当另一个后面接入的端也得有sps和pps才能渲染出相应的画面。(项目还集成了封装rtmp数据的,我处理的方式是编码后的数据,SOCKET的拿到数据做相关处理,RTMP也是拿这个编码后数据进行处理,相应是将数据COPY为两份去处理)
出现问题:是先推送数组给其他方,再用另一个局域网手机进行连接播放页面出现bug的。
(1)接收端使用DataInputStream接收数据,在使用ReadInt方法要么接收到的数值为负数,要么就是数值很大,因为在接收后还创建了byte数组进行接收数据的,负数过滤好像是这样,但数值过大的创建数组就会造成OOM了,所以相关头痛治头,脚疼医脚的道理。加一个过滤条件吧。
条件为:接收的int大于0 且 小于等于Socket最大接收的值(在连接Socket时设置的了)
设置条件后的现象:程序不崩,也不OOM了。但画面严重卡顿,因为很多不符合过滤条件的数据都被过滤了,所以画面卡。
(2)继续在上面的条件上查找解决方法:有些方案是使用skipBytes跳过几个字节,但又说每个设备跳过的字节都不一样。这坑让我无法跨过啊。直接写跳过8个字节,现象是画面更卡了。
(3)既然接收端无法解决就看一下发送端了:DataOutputStream先是writeInt 然后flush再写write(byte[]) flush,改一下为write(byte[] ,0,date.length)。结果还是那样。
(4)再看一下发送的数据(int type=data[4] &0x1f 根据这个type来判断是正式数据或者sps的数组或者 是其他数据):sps和Pps的数组(类型7或者类型8)与视频正常数据(类型为5)每次都是先发sps的数组再发正式数据的(出现接收错乱的是这里造成的),想着改一下,将sps数组与正式数据合并为一个数组再发送,然后上面修改我还保留着(不想因为数值的问题造成SOCKET断开连接之类的,所以保留下来而已)。现象正常接收和播放了。
30.BottomNavigationView挡住了Fragment的底部的显示
1).在每个Fragement的最外层添加如下
android:paddingBottom="?attr/actionBarSize" android:clipToPadding="false"
2).在Activity中设置高度问题。
控件fragment的高度直接设置为0db,而不是match_parent.
参考:BottomNavigationView底部导航遮挡fragment解决方法_bottomnavigationview 遮挡-CSDN博客
31.运行测试代码
1.环境配置
testImplementation 'androidx.test:core-ktx:1.5.0'
testImplementation 'org.robolectric:robolectric:4.10.3'
类的顶部添加环境
@RunWith( RobolectricTestRunner::class)
@Config(manifest = Config.NONE ,sdk = [Build.VERSION_CODES.P])
class CustomConfigTest{
@Test fun test(){ val context: Context = ApplicationProvider.getApplicationContext() //设置获取资源的语言环境 val resources: Resources = RuntimeEnvironment.application.resources val config = Configuration(resources.getConfiguration()) config.setLocale(Locale.SIMPLIFIED_CHINESE) // 设置为简体 resources.updateConfiguration(config, resources.getDisplayMetrics()) AppConfigUtil.dealGenJson(context) } } 2.将相应内容生成文件
@Throws(IOException::class) fun generateFileInProjectDir(jsonStr: String, jsonFileName: String) { // 获取项目根目录 val projectDir = System.getProperty("user.dir") // 创建测试输出目录 val outputDir = File("$projectDir/test-output") if (!outputDir.exists()) { outputDir.mkdirs() } // 创建文件并写入内容 val outputFile = File(outputDir, jsonFileName) FileWriter(outputFile).use { writer -> writer.write(jsonStr) } println("文件已生成: " + outputFile.absolutePath) }