Android小提示五
@[TOC]
【68.让ProgressDialog在setCancelable(false)时按返回键可dismiss】
主要是为 ProgressDialog 添加 KeyListener 来对返回键予以处理
(福利推荐:阿里云、腾讯云、华为云服务器最新限时优惠活动,云服务器1核2G仅88元/年、2核4G仅698元/3年,点击这里立即抢购>>>)
private ProgressDialog progressDialog = null; /** * show loading progress dialog */ public void showDialog() { if (null == progressDialog) { progressDialog = ProgressDialog.show(BaseActivity.this, "", "正在加载,请稍候..."); progressDialog.setCancelable(false); } else { progressDialog.show(); } progressDialog.setOnKeyListener(onKeyListener); } /** * add a keylistener for progress dialog */ private OnKeyListener onKeyListener = new OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { dismissDialog(); } return false; } }; /** * dismiss dialog */ public void dismissDialog() { if (isFinishing()) { return; } if (null != progressDialog && progressDialog.isShowing()) { progressDialog.dismiss(); } } /** * cancel progress dialog if nesseary */ @Override public void onBackPressed() { if (progressDialog != null && progressDialog.isShowing()) { dismissDialog(); } else { super.onBackPressed(); } }
【dialog监听back】
dialog.setCanceledOnTouchOutside(false); //外部不行,back可以 dialog.setOnCancelListener(dialog->{ //监听back按钮的 })
【69.传递数据 Intent,LiveDataBus,EventBus】
- 1.bundle内部实现是ArrayMap,在小数据存储的时候,效率比Hashmap高,而一般需要使用bundle的场景数据都比较小。
- 2.bundle使用ParceLable序列化对象,而Hashmap是java的类,使用的是Serializable,效率上bundle高。
[https://blog.csdn.net/H291850336/article/details/50515705]()
在跨多个Activity进行传递数据时最好不要用Intent,容易造成内存泄漏。
- 3.String类型效率比Serializable,Parcelable低。
枚举Enum序列化
private MyBean(Parcel in) { mField = in.readInt();//普通整型 mMyEnum = MyEnum.values()[in.readInt()];//枚举类型,此枚举类不需要实现Parcelable } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mField);//普通整型 dest.writeInt(mMyEnum.ordinal());//枚举类型 }
通信方案 | 优点 | 缺点 |
---|---|---|
handler | 系统原生,能实现线程间通信 | 高耦合 不利于维护 容易导致内存泄漏和空指针 |
broadcast | 简单 | 性能差 传播数据有限 打乱代码的执行逻辑 |
interface | 速度快,容易理解 | 实现复杂,不利于维护 |
rxBus | 效率高,无内存泄漏 | 基于rxjava,学习成本高且依赖包太大,rxjava2.2M |
EventBus | 使用简单 | 混淆问题 无法感知组件生命周期 实现复杂 |
LiveDataBus | 实现极其简单,代码量少 官方提供稳定的依赖代码 感知组件生命周期 不会造成内存泄漏 |
LiveDataBus:核心部分
Lifecycle
Lifecycle是Android官方推出的架构之一,它具有生命周期感知功能,不但能够监听Activity和Fragment的生命周期,还能回调相应方法,同时能够实时的获取当前Activity和Fragment的状态。
LiveData
LiveData是一个数据持有类,持有数据并且这个数据能够被观察者所监听到,而且他是和Lifecycle绑定的,具有生命周期感知,解决内存泄露和引用问题。
什么是Hook技术?
Hook即“钩子”,他可以在事件传送中截获并监控事件传输,将自身的代码与系统方法进行融入。这样当方法被调用时,也就可以执行我们自己的代码,这也是面向切面编程的思想(AOP)。
其实就是通过反射获取到“Hook”点,然后在Hook点执行我们要插入的方法或者改变其原本逻辑的参数,然后在接着执行它原本要执行的逻辑。
【70.资源警告No package identifier】
去除 No package identifier when getting name for resource number 0x00000000 错误信息
将所有颜色信息移动到color.xml内避免此错误
Glide位图警告 (未解决):
[https://github.com/bumptech/glide/issues/743]()
W/Bitmap: Called reconfigure on a bitmap that is in use! This may cause graphical corruption!
旧版 | glide 4.9.0 |
---|---|
crossFade(500) | transition(DrawableTransitionOptions.withCrossFade(500)) |
transform(new GlideCircleTransform(v.getContext())) | transform(new GlideCircleTransform()) |
bitmapTransform(new BlurTransformation(this, 23, 4)) | apply(bitmapTransform(new BlurTransformation( 50, 8))) |
listener(new RequestListener<String, GlideDrawable>()… | listener(new RequestListener()… |
- glide 4.9.0 可以直接配置圆形和圆角图片 transforms(new CircleCrop())
- 渐变设置和监听设置有更改
- asBitmap() 需要设置在 load(url)之前
// 官方 Glide implementation 'com.github.bumptech.glide:glide:4.9.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' // 高斯模糊和圆角等 implementation 'jp.wasabeef:glide-transformations:4.0.1'
GLide参考:[https://cloud.tencent.com/developer/article/1424574]()
[https://www.jianshu.com/p/fd464ce87c79]()
[https://muyangmin.github.io/glide-docs-cn/doc/getting-started.html?userCode=wrvvs1rm]()
替换Glide通讯组件为Okhttp并监控加载进度
[https://blog.csdn.net/huangxiaoguo1/article/details/78595627]()
【72.Handler警告】
- 非静态Handler导致Activity泄漏
初始: public class ActivityA extends AppCompatActivity{ //泄露,一般置为 static private Handler handler = new Handler(){ @Override public void handleMessage(Message msg){ super.handleMessage(msg); } } @Override public void onCreate(Bundle savedInstanceState){ handler.postDelayed(new Runnable(){ @Override public void run(){ try{ Thread.sleep(10000); } catch(Exception e){...} } },3000); } } 修改: private MyHandler handler; protected void setUpData() { handler = new MyHandler(this); handler.sendEmptyMessageDelayed(0, 1000); } @Override protected void onPause() { super.onPause(); handler.removeMessages(0); } static class MyHandler extends Handler { private final WeakReference<Activity> mActivity; public MyHandler(Activity activity) { this.mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Activity activity = mActivity.get(); if (activity != null) { } } }
【73.List集合 containsAll 方法】
1. java中的list是有contains方法: 判断列表中是否包含指定元素。如果列表中包含指定元素,则返回true,否则返回false。 list.add("菠萝"); //向列表中添加数据 String o = "苹果"; list.contains(o); //返回false 2. java中的list是有containsAll方法: 判断B链表是不是A链表的子集,我们可以使用A.containsAll(B)来判断, 当返回值是true的时候就表明B链表是A链表的子集, 当返回值是false时候就表明B链表不是A链表的子集。 ArrayList<String> als = new ArrayList<String>(); als.add("a"); als.add("b"); ArrayList<String> alss = new ArrayList<String>(); alss.add("a"); alss.add("c"); System.out.println(als.containsAll(alss)); 实验结果:false 源代码: public boolean containsAll(Collection<?> c) { Iterator<?> e = c.iterator(); while (e.hasNext()) if (!contains(e.next())) return false; return true; }
【74.判断是否为 null】
//其中null代表某个变量 if("0".equals(null)){ //可以判断 } if(null.equals("0")){ //会崩溃 } Integer null==1,还是 1==null都崩溃
【75.设置字体】
Android系统默认字体支持四种字体,分别为:
- noraml (普通字体,系统默认使用的字体)
- sans(非衬线字体)
- serif (衬线字体)
- monospace(等宽字体)
关于后三种字体的区别可以看:
[http://kb.cnblogs.com/page/192018/]()
- 在xml中修改字体
<!-- 使用默认的sans字体--> <TextView android:id="@+id/sans" android:text="Hello,World" android:textSize="20sp" android:typeface="sans" /> <!-- 使用默认的serifs字体--> <TextView android:id="@+id/serif" android:text="Hello,World" android:textSize="20sp" android:typeface="serif" /> <!-- 使用默认的monospace字体--> <TextView android:id="@+id/monospace" android:text="Hello,World" android:textSize="20sp" android:typeface="monospace" />
- 在java代码中修改字体
//设置serif字体 textView.setTypeface(Typeface.SERIF); //设置sans字体 textView.setTypeface(Typeface.SANS_SERIF); //设置monospace字体 textView.setTypeface(Typeface.MONOSPACE);
- 在Android中可以引入其他字体
在assets目录下新建fonts目录,把ttf字体文件放到这
//得到AssetManager AssetManager mgr=getAssets(); //根据路径得到Typeface Typeface tf=Typeface.createFromAsset(mgr, "fonts/pocknum.ttf"); //在实际使用中,字体库可能存在于SD卡上,可以采用createFromFile()来替代createFromAsset。 //String path =Environment.getExternalStorageDirectory().getAbsoluteFile() + File.separator + "xxx.ttf"; //Typeface typeface2 =Typeface.createFromFile(path); //设置字体 textView.setTypeface(tf);
【76.布局优化】
Android布局优化:include 、merge、ViewStub的详细总结
merge标签必须使用在根布局,并且ViewStub标签中的layout布局不能使用merge标签.
事件拦截:
https://blog.csdn.net/weixin_37228152/article/details/103865151
【77.判断当前APP后台运行】
切换回界面输入密码等
//判断当前APP后台运行 private boolean isAppBg(Context context) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); //这是把所有进程全部取出了,【不行】 List<ActivityManager.RunningAppProcessInfo> appProcessList = am.getRunningAppProcesses(); if (appProcessList == null) { return false; } for (ActivityManager.RunningAppProcessInfo appProcess : appProcessList) { if (appProcess.processName.equals(context.getPackageName())) { if (appProcess.importance != ActivityManager.RunningAppProcessInfo .IMPORTANCE_FOREGROUND) { Log.e(context.getPackageName(), "处于后台" + appProcess.processName); return true; } else { Log.e(context.getPackageName(), "处于前台" + appProcess.processName); return false; } } } return false; }
利用这个手机 多任务单进程,只会有一个在前台显示
public class BaseApplication extends Application { private int appCount = 0; @Override public void onCreate() { super.onCreate(); this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { } @Override public void onActivityStarted(@NonNull Activity activity) { appCount++; } @Override public void onActivityResumed(@NonNull Activity activity) { } @Override public void onActivityPaused(@NonNull Activity activity) { } @Override public void onActivityStopped(@NonNull Activity activity) { appCount--; if (appCount == 0) { Toast.makeText(getApplicationContext(),"切入后台",Toast.LENGTH_SHORT).show(); } } @Override public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { } @Override public void onActivityDestroyed(@NonNull Activity activity) { } }); } }
【78.Android第一次安装后Home键重启问题】未验证
Android应用第一次安装成功点击“打开”后Home键切出应用后再点击桌面图标返回导致应用重启问题
//isTaskRoot是Activity系统方法 if (!this.isTaskRoot()) { Intent mainIntent = getIntent(); String action = mainIntent.getAction(); if (mainIntent.hasCategory(Intent.CATEGORY_LAUNCHER) && action.equals(Intent.ACTION_MAIN)) { finish(); return; } } @Override public void onCreate(Bundle savedInstanceState){ //Android第一次安装打开,home键再点击启动,程序重复启动 if (!isTaskRoot()){ finish(); return; } }
【番外:引入第三方包报错】
https://blog.csdn.net/chenlove1/article/details/60958886
【79.一些属性-横竖屏】
a)Aandroid设置横屏和竖屏的方法:
AndroidManifest.xml中:android:screenOrientation "unspecified":默认值 由系统来推断显示方向.判定的策略是和设备相关的,所以不同的设备会有不同的显示方向. "landscape":横屏显示(宽比高要长) "portrait":竖屏显示(高比宽要长) "user":用户当前首选的方向 "behind":和该Activity以下的那个Activity的方向一致(在Activity堆栈中的) "sensor":有物理的感应器来决定。假设用户旋转设备这屏幕会横竖屏切换。 "nosensor":忽略物理感应器。这样就不会随着用户旋转设备而更改了("unspecified"设置除外)。
方法二:在java代码中设置
设置横屏代码:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//横屏 设置竖屏代码:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏 if(this.getResources().getConfiguration().orientation ==Configuration.ORIENTATION_PORTRAIT){ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }
b)android:configChanges属性
一些设备的配置可能会改变,如:横竖屏的切换、键盘的可用性等。这些事件一旦发生,当前活动的Activity会重新启动,其中的过程是:在销毁之前会先调用onSaveInstanceState()方法去保存你应用中的一些数据,然后调用onDestroy()方法,最后调用onCreate()、onStart()、onResume()等方法启动一个新的Activity。
“screenLayout”: 屏幕的显示发生了变化——不同的显示被激活
“screenSize”: 屏幕大小改变了
https://blog.csdn.net/hanyingjie327/article/details/21246545
AlarmManager定时闹钟的用法:
[https://www.cnblogs.com/ProtectedDream/p/6351447.html?userCode=wrvvs1rm]()
【80.if 与 switch】
- 1.当分支较多时,当时用switch的效率是很高的。因为switch是随机访问的,就是确定了选择值之后直接跳转到那个特定的分支,但是if。。else是遍历所以得可能值,知道找到符合条件的分支。如此看来,switch的效率确实比ifelse要高的多。
- 2.由汇编代码可知道,switch…case占用较多的代码空间,因为它要生成跳表,特别是当case常量分布范围很大但实际有效值又比较少的情况,switch…case的空间利用率将变得很低。
- 3.switch…case只能处理case为常量的情况,对非常量的情况是无能为力的。例如 if (a > 1 && a < 100),是无法使用switch…case来处理的。所以,switch只能是在常量选择分支时比ifelse效率高,但是ifelse能应用于更多的场合,ifelse比较灵活。
一般5个选项(包括default)的情况下,switch和if/else if相同。低于5个选项if快,高于5给选项switch快。
在AndroidLibrary中view.getId不能用switch分支:
原因是:Resource IDs cannot be used in a switch statement in Android library modules
在Android library中不能使用switch-case语句访问资源ID,问题的原因是Android library中生成的R.java中的资源ID不是常数
在library中通过if-else-if条件语句来引用资源ID,这样就避免了这个错误。public void onClick(View src){ int id = src.getId(); if (id == R.id.playbtn){ // ... } else if (id == R.id.stopbtn){ // ... } else if (id == R.id.btnmenu){ openOptionsMenu(); } }
【81.通知栏权限关闭 吐司不显示】
小米显示,华为,三星,魅族,乐视等不显示
Toast通知栏权限填坑指南
Android部分手机通知权限关闭无法打出Toast
Toast怎么支持点击事件?[https://github.com/getActivity/XToast]() 使用
// 判断是否为 Android 6.0 及以上系统并且有悬浮窗权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(mToast.getView().getContext())) { // 解决使用 WindowManager 创建的 Toast 只能显示在当前 Activity 的问题 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; }else { params.type = WindowManager.LayoutParams.TYPE_PHONE; } } /** * 检查通知栏权限有没有开启 */ public static boolean isNotificationEnabled(Context context){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)).areNotificationsEnabled(); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); ApplicationInfo appInfo = context.getApplicationInfo(); String pkg = context.getApplicationContext().getPackageName(); int uid = appInfo.uid; try { Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName()); Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class); Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION"); int value = (Integer) opPostNotificationValue.get(Integer.class); return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0; } catch (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException | ClassNotFoundException ignored) { return true; } } else { return true; } }
注释的一些小技巧:[https://www.cnblogs.com/wangyun/p/9176356.html?userCode=wrvvs1rm]()
你还在原价购买阿里云、腾讯云、华为云、天翼云产品?那就亏大啦!现在申请成为四大品牌云厂商VIP用户,可以3折优惠价购买云服务器等云产品,并且可享四大云服务商产品终身VIP优惠价,还等什么?赶紧点击下面对应链接免费申请VIP客户吧:
相关文章
- Jitpack发布Android库出现Direct local .aar file dependencies are not supported when building an AAR
- 关于Android12安装apk出现-108异常INSTALL_PARSE_FAILED_MANIFEST_MALFORMED的解决方法
- Android 多module情况下module依赖aar问题处理
- Android7.0以上的分享图片文件错误及解决方法
- Android开发 对接微信分享SDK总结
- Android RecyclerView使用ListAdapter高效刷新数据
- Android自动化测试工具调研
- Android 自动取色并设置沉浸式状态栏
- Java 断点下载(下载续传)服务端及客户端(Android)代码
- Android自定义View学习(1)——基础知识介绍
- Android webview只加载10%且出现白屏问题排查解决
- Android Studio安装插件重启插件消失
- Android Swtich开关样式调整
- Android 实现开机自启APP
- 关于TornadoFx和Android的全局配置工具类封装实现及思路解析
- Android shape与selector标签使用
- Android8.0 后台服务保活的一种思路
- 关于Android安装apk出现解析包异常问题情况总结
- Android 接入腾讯IM即时通信(详细图文)
- Android 语音播放(文字TTS)