三星图片旋转
独三星的手机拍照之后,你会很清楚的看到会把照片旋转一下,然后你根据路径找到的图片就是已经被旋转的了。处理方式:获取图片的exif(Exchangeable Image File 可交换图像文件)信息中的旋转角度。如果被旋转了,那么就进行逆向选择
public static int readPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
public static Bitmap toturn(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
int width = img.getWidth();
int height = img.getHeight();
img = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
return img;
}
重复创建快捷方式
很多手机厂商取消了快捷方式的概念,导致我们无法通过代码创建一个我们需要的快捷方式
对于这些不支持创建快捷方式的手机,咱就忽略了~
Android 的 Launcher 源码在创建快捷方式的时候不仅会判断 duplicate 的值,还会在数据库中查询一下将要被创建的快捷方式是否已经存在,我们也照做就 OK 了
但是众多厂商的url不尽相同
2.2 版本以前的URI 是:content://com.android.launcher.settings/favorites?notify=true
2.2~4.3 版本的URI 是:content://com.android.launcher2.settings/favorites?notify=true
4.4 版本以上的目前都是:content://com.android.launcher3.settings/favorites?notify=true
还有特定机型的URL也不同,这里就不列举了~
通过权限查询URI
ListView设置分割线与内边距
TextView展示小表情
//将发布的评论由Base64编码还原为字符串,为了兼容表情符
if (data.getContent().contains("E5BE88E5B18C")) {
byte[] decodeBase64 = Base64.decodeBase64(data.getContent().replace("E5BE88E5B18C", ""));
content.setText(new String(decodeBase64));
} else {
content.setText(com.emotion.StringUtils.getEmotionContent(MomentsDetailsActivity.this, content, data.getContent()));
}
通过名称获取资源id
int lebId = Resources.getSystem()
.getIdentifier("permlab_accessNetworkState",
"string", "android");
String lab = getString(lebId);
WebView blocks redirect from https to http
Added in API level 21
Used with setMixedContentMode(int) In this mode, the WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. This is the least secure mode of operation for the WebView, and where possible apps should not set this mode.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
弹出软键盘问题
有个需求:比如让你一进入页面就弹出软键盘,网上一搜一堆代码,发现就是弹不出来。为什么?因为可能你的目标view当时还没有完成完整绘制流程
getHandler().postDelayed(new Runnable() {
@Override
public void run() {
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(activity.getWindow().getDecorView().getWindowToken(), 0);
}
},50);
webview调用js报线程错误
W/WebView(2088): Java.lang.Throwable: A WebView method was called on thread ‘JavaBridge’. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {b3dbcb18} called on Looper (JavaBridge, tid 120) {b44a1af8}, FYI main Looper is Looper (main, tid 1) {b3dbcb18})
W/WebView(2088): at android.webkit.WebView.checkThread(WebView.java:2063)
W/WebView(2088): at android.webkit.WebView.loadUrl(WebView.java:794)
W/WebView(2088): at com.ue.oa.activity.XFormActivity.alert(XFormActivity.java:180)
W/WebView(2088): at com.ue.oa.activity.XFormActivity$FormActions.save(XFormActivity.java:193)
W/WebView(2088): at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
W/WebView(2088): at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
W/WebView(2088): at android.os.Handler.dispatchMessage(Handler.java:102)
W/WebView(2088): at android.os.Looper.loop(Looper.java:136)
W/WebView(2088): at android.os.HandlerThread.run(HandlerThread.java:61)
webView.post(new Runnable() {
@Override
public void run() {
// your code
}
});
Warning Exception while processing IOException Please correct
混淆规则中加入
-ignorewarnings # 抑制警告
Activity启动模式和onActivityResult之间的冲突
public void startActivityForResult (Intent intent, int requestCode, Bundle options)
Added in API level 16
Launch an activity for which you would like a result when it finished. When this activity exits, your onActivityResult() method will be called with the given requestCode. Using a negative requestCode is the same as calling startActivity(Intent) (the activity is not launched as a sub-activity).
Note that this method should only be used with Intent protocols that are defined to return a result. In other protocols (such as ACTION_MAIN or ACTION_VIEW), you may not get the result when you expect. For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.
As a special case, if you call startActivityForResult() with a requestCode >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your activity, then your window will not be displayed until a result is returned back from the started activity. This is to avoid visible flickering when redirecting to another activity.
This method throws ActivityNotFoundException if there was no Activity found to run the given Intent.
如果运行所给Intent的Activity没被找到,该方法会抛出ActivityNotFoundException异常
解决方案
singleTop模式,可用来解决栈顶多个重复相同的Activity的问题
singleTask模式和后面的singleInstance模式都是只在另外一个任务栈中
所以当使用singleTask与singleInstance模式会有上述问题
moveTaskToBack退后台
方法:public boolean moveTaskToBack(boolean nonRoot)
activity里有这个方法,参数说明如下:
nonRoot=false→ 仅当activity为task根(即首个activity例如启动activity之类的)时才生效
nonRoot=true→ 忽略上面的限制
这个方法不会改变task中的activity中的顺序,效果基本等同于home键
应用场景:
比如有些activity诸如引导图之类的,用户在按返回键的时候你并不希望退出(默认就finish了),而是只希望置后台,就可以调这个方法
解决EditText自动获取焦点问题
问题分析:EditText自动获取焦点,导致进入页面时自动弹出输入法
解决方法:设置其他控件可获取焦点并允许通过触摸获取焦点
......
immutable bitmap passed to Canvas constructor
出现Immutable bitmap passed to Canvas constructor错误的原因是如果不用copy的方法,直接引用会对资源文件进行修改,而Android是不允许在代码里修改res文件里的图片
BitmapFactory.decodeResource(getResources(), R.drawable.xiao).copy(Bitmap.Config.ARGB_8888, true);
ScrollView嵌套 ListView GridView 引发的自动滑动问题
ScrollView嵌套 listView gridView 引发的自动滑动 尤其当listView或gridView在屏幕底部或超出屏幕时尤为明显。一般出现这种情况是焦点问题,这时如果不想listView或girdView获取焦点的话,需要在ScrollView下的根布局设置
android:descendantFocusability=”blocksDescendants”
设置之后 就不会自动滑动到底部去了 我是这样就解决的
descendantFocusability有三种属性
beforeDescendants :viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
退出所有Activity 杀死应用 结束应用
1 抛出运行时异常:使程序崩溃退出
2 System.exit(0)
以上两种方法:过于极端,可能会导致后台的服务等也随着退出
3 android.os.Process.killProcess(Process.myPid())
4 通过ActivityManager来结束指定包名的所有界面
5 利用设置Intent的FLAG参数为FLAG_ACTIVITY_CLEAR_TOP
当启动某个Activity时会清除其上方的所有Activity
6 创建自己的ActivityManager统一管理所有Activity
在BaseActivity的onCreate方法中调用添加方法
在BaseActivity的onDestroy方法中调用移除方法
拍照裁减图片时出现SecurityException
你在跳转activity的过程中携带的extras中有图片的Bitmap,应用尽量减少图片的尺寸大小,或者通过保存图片到sd卡中或者通过uri方式传递图片参数
open failed EBUSY (Device or resource busy)
处理Android文件的时候遇到了这样一个问题:当删除一个文件后,无法再次创建相同名称的文件。通过捕获异常可以发现系统爆出了open failed: EBUSY (Device or resource busy)的异常,大致是改文件仍在操作中,无法进行其他操作的意思。StackOverFlow上说,是由于android系统的原因,导致删除的时候并没有释放文件锁,从而导致无法再次创建。
解决方案:先对要删除的文件进行重命名,然后再删除。这样删除过程中的文件锁就加在另一个文件上了,不会影响再次创建的过程。
final File to = new File(file.getAbsolutePath() + System.currentTimeMillis());
file.renameTo(to);
to.delete();
最优单例
1.实现了懒汉模式,在需要的时候才创建
2.避免了多线程并发访问导致的问题
3.解决了代码重拍优化导致的问题
public class ClearViewHelper {
private void ClearViewHelper(){}
public static ClearViewHelper getInstance(){
return ClearViewHelperHolder.clearViewHelper;
}
private static class ClearViewHelperHolder{
public static ClearViewHelper clearViewHelper = new ClearViewHelper();
}
}
adapter.notifyDataSetChanged无效
问题:adapter.notifyDataSetChanged没有反应,要触摸屏幕才可以改变数据
原因:数据集合可能正在被操作(增/删/改/查)
解决:延时调用或者确保在数据稳定后调用
如何生成一个二维码同时支持多种支付方式
问题:如何生成一个二维码,同时支持多种支付方式(如:微信支付、支付宝、百度钱包等等)
分析:每个支付平台他们所使用的都是他们自己的规范,他们之间的二维码规范是相互独立的。本质上来说,这个需要是无法实现的,但是可以通过间接的方式
方案:生成的二维码存放支付链接,扫描打开后就是支付页面,提供相关的支付方式给用户选择
Fragment调用getActivity为null的问题
很多人都曾被这个问题所困扰,如果app长时间在后台运行,再次进入app的时候可能会出现crash,而且fragment会有重叠现象。如果系统内存不足、切换横竖屏、app长时间在后台运行,Activity都可能会被系统回收然后重建,但Fragment并不会随着Activity的回收而被回收,创建的所有Fragment会被保存到Bundle里面,从而导致Fragment丢失对应的Activity
隐式意图的判断问题
问题:当我们使用隐式意图的时候有可能报错
分析:当我们采用隐式意图去调用时,系统所有会进行意图的匹配(包括action、category以及data),那么匹配结果是有可能匹配不到的,那么此时就会报错
ComponentName componentName = intent.resolveActivity(getPackageManager());
if(null == componentName){
// Intent匹配失败
}
PackageManager pm = this.getPackageManager();
List resolveInfos = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if(null == resolveInfos){
// Intent匹配失败
}
两种方式的区别
resolveActivity方式返回的是最佳匹配的Activity
queryIntentActivities返回的是所有匹配成功的Activity
PopupWindow弹出导致ANR
popupWindow.setBackgroundDrawable(new BitmapDrawable());
保存图片并且更新相册
/**
* 保存方法
*/
public void saveBitmap() {
if (bitmap == null) {
bitmap = drawableToBitmap(image.getDrawable());
}
image.getDrawable();
File f = new File(Environment.getExternalStorageDirectory() + "/hongwu/image", System.currentTimeMillis() + ".jpg");
if (f.exists()) {
f.delete();
}
try {
FileOutputStream out = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
Toast.makeText(this, "图片已保存至/storage/image/ 文件夹", Toast.LENGTH_SHORT).show();
UpdateAlbum(EaseShowBigImageActivity.this, f.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void UpdateAlbum(Context context, String saveImagePath) {
int androidVersion = Build.VERSION.SDK_INT;
if (androidVersion >= 19) {
MediaScannerConnection.scanFile(context,
new String[]{saveImagePath}, null,
new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String path, Uri uri) {
}
});
} else {
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://" + saveImagePath)));
}
}
/**
* Drawable转化为Bitmap
*/
public static Bitmap drawableToBitmap(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
return bitmap;
}
finish() onDestroy() 和System.exit()的区别
Activity.finish()
Call this when your activity is done and should be closed.
在你的activity动作完成的时候,或者Activity需要关闭的时候,调用此方法。
当你调用此方法的时候,系统只是将最上面的Activity移出了栈,并没有及时的调用onDestory()方法,其占用的资源也没有被及时释放。因为移出了栈,所以当你点击手机上面的“back”按键的时候,也不会再找到这个Activity。
Activity.onDestory()
the system is temporarily destroying this instance of the activity to save space.
系统销毁了这个Activity的实例在内存中占据的空间。
在Activity的生命周期中,onDestory()方法是他生命的最后一步,资源空间等就被回收了。当重新进入此Activity的时候,必须重新创建,执行onCreate()方法。
System.exit(0)
这玩意是退出整个应用程序的,是针对整个Application的。将整个进程直接KO掉。
commitAllowingStateLoss
你在onSaveInstanceState()之后调用FragmentTransaction#commit()的时候,transation不会被记录。因为它不会作为之前Activity的状态被保存。从用户的角度来说,这个transaction就像丢失了,导致UI状态意外的丢失。为了保证用户体验,Android不计一切代价避免状态丢失,也就是当它发生的时候简单地抛出一个IllegalStateException
可使用commitAllowingStateLoss()作为解决方案
关于获取当前Activity
在Android开发过程中,我们有时候需要获取当前的Activity实例,比如弹出Dialog操作,必须要用到这个。关于如何实现由很多种思路,这其中有的简单,有的复杂,这里简单总结一下
反射
反射是我们经常会想到的方法,思路大概为
1 获取ActivityThread中所有的ActivityRecord
2 从ActivityRecord中获取状态不是pause的Activity并返回
然而这种方法并不是很推荐,主要是有以下的不足:
- 反射通常会比较慢
- 不稳定性:这个才是不推荐的原因,Android框架代码存在修改的可能性,无法保证mActivities,paused固定不变。所以可靠性不是完全可靠
Activity基类
既然反射不是很可靠,那么有一种比较可靠的方式,就是使用Activity基类
在BaseActivity的onResume方法中,将当前Activity实例保存到一个变量中
public class BaseActivity extends Activity{@OverrideprotectedvoidonResume(){
super.onResume();
MyActivityManager.getInstance().setCurrentActivity(this);
}
}
然而,这一种方法也不是完美的,因为这种方法是基于约定的;要求每个Activity都必须继承BaseActivity,若出现非继承BaseActivity的就可能有问题
回调方法
Android 自 API 14 开始引入了一个方法
即Application的registerActivityLifecycleCallbacks方法
用来监听所有Activity的生命周期回调
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { }
@Override
public void onActivityStarted(Activity activity) { }
@Override
public void onActivityResumed(Activity activity) {
MyActivityManager.getInstance().setCurrentActivity(activity); }
@Override
public void onActivityPaused(Activity activity) { }
@Override
public void onActivityStopped(Activity activity) { }
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { }
@Override
public void onActivityDestroyed(Activity activity) { }
});
}
}
public class MyActivityManager {
private static MyActivityManager mManager;
private WeakReference mReference;
public MyActivityManager() {
}
public static MyActivityManager getInstance() {
if (mManager == null)
synchronized (String.class) {
if (mManager == null)
mManager = new MyActivityManager();
}
return mManager;
}
public void setCurrentActivity(Activity activity) {
mReference = new WeakReference(activity);
}
public Activity getCurrentActivity(Activity activity) {
if (mReference != null)
if (mReference.get() != null)
return mReference.get();
return null;
}
}
那么为什么要使用弱引用持有Activity实例呢?
其实最主要的目的就是避免内存泄露,因为使用默认的强引用会导致Activity实例无法释放,导致内存泄露的出现
跳转目标页面并清除之上页面
业务需求:从 A -> B -> C -> D页面后从D页面跳转到A页面并清除BCD页面
解决方式:
1 设置Intent的flags为FLAG_ACTIVITY_CLEAR_TOP
2 设置目标页面(即A)的launchMode为singleTask
作用:当目标界面在栈中已存在时则复用并清除在它之上的所有Activity
关于Intent.makeRestartActivityTask
问题:使用 PendingIntent 如何进入多层页面
一般来讲,点击一个notification后,都会打开一个Activity做为对点击事件的响应,这个Activity是之前在PendingIntent中设置好的
经常玩Android手机的应该都有印象,在日历应用中,你新建一个提醒,当提醒通知收到后,你点击通知,会进入提醒的内容页面,如果这个时候按back键,会直接退出应用
但是在Gmail的应用中,如果有一封新邮件到来,那么点击通知后,会进入到邮件的内容页面,等你看完邮件,点击back键,会退到邮件列表页面,再按back键,才会退出应用
第一种情况:
点击Notification ——>进入SubActivity ——> back键 ——> 退出应用
第二种情况:
点击Notification ——>进入SubActivity ——> back键 ——> 退到ParentActivity ——>back键 ——>退出应用
第一种情况比较简单,只需要在PendingIntent中指定Activity
PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
第二种情况 PendingIntent.getActivities + makeRestartActivityTask
第二种情况稍微复杂,因为如果只打开一个SubActivity,程序没办法知道它的上一级Activity是谁,所以需要在点击Notification时打开一组Activity
PendingIntent提供了个静态方法getActivities,里面可以设置一个Intent数组,用来指定一系列的Activity
PendingIntent pendingIntent = PendingIntent.getActivities(this, 0x6666, makeIntentStack(this), PendingIntent.FLAG_CANCEL_CURRENT);
mManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 35000, pendingIntent);
private Intent[] makeIntentStack(Context context) {
Intent[] intents = new Intent[4];
// makeRestartActivityTask 用来重启应用程序的任务栈
intents[0] = Intent.makeRestartActivityTask(new ComponentName(context, MainActivity.class));
intents[1] = new Intent(context, Main2Activity.class);
intents[2] = new Intent(context, Main3Activity.class);
intents[3] = new Intent(context, Main4Activity.class);
return intents;
}
独立进程导致Application实例多次
独立进程会导致Application多次实例化
执行多次onCreate,导致相关资源重复初始化
解决方式:通过判断当前进程名称有选择地进行相关操作
public void onCreate() {
super.onCreate();
String nowProcessName = ProcessUtil.getProcessName(this, Process.myPid());
if (SERVICE_PROCESS_NAME.equals(nowProcessName)) {
init();
}
}
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
xml布局中的权重问题
android:layout_weight=”3”
权重 weight 的取值含义:占据剩余空间的比例
注意:权重值越大,渲染优先级越低(后渲染)
权重值越小,渲染优先级越高(先渲染)
可滚动的大图ImageView
思路:HorizontalScrollView + 大尺寸ImageView
解析:滚动View中放置相对布局,在相对布局中又放置大尺寸的ImageView,实现可滚动的大图ImageView
Fragment嵌套使用存在的问题
问题1:当使用Fragment去嵌套另外一些子Fragment的时候,我们需要去管理子Fragment,这时候需要调用ChildFragmentManager去管理这些子Fragment,由此可能产生的Exception主要是:
java.lang.IllegalStateException: No activity
首先我们来分析一下Exception出现的原因:
通过DEBUG发现,当第一次从一个Activity启动Fragment,然后再去启动子Fragment的时候,存在指向Activity的变量,但当退出这些Fragment之后回到Activity,然后再进入Fragment的时候,这个变量变成null,这就很容易明了为什么抛出的异常是No activity
这个Exception是由什么原因造成的呢?如果想知道造成异常的原因,那就必须去看Fragment的相关代码,发现Fragment在detached之后都会被reset掉,但是它并没有对ChildFragmentManager做reset,所以会造成ChildFragmentManager的状态错误
找到异常出现的原因后就可以很容易的去解决问题了,我们需要在Fragment被detached的时候去重置ChildFragmentManager,即:
@Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class
.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
问题2:当我们从一个Activity启动了一个Fragment,然后在这个Fragment中又去实例化了一些子Fragment,在子Fragment中去有返回的启动了另外一个Activity,即通过startActivityForResult方式去启动,这时候造成的现象会是,子Fragment接收不到OnActivityResult,如果在子Fragment中是以getActivity.startActivityForResult方式启动,那么只有Activity会接收到OnActivityResult,如果是以getParentFragment.startActivityForResult方式启动,那么只有父Fragment能接收(此时Activity也能接收),但无论如何子Fragment接收不到OnActivityResult
解决方式:
方式1:使用getActivity的startActivityForResult方法去启动
在Activity的onActivityResult方法中调用相应父fragment的onActivityResult方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println(resultCode + " activity");
mFragment.onActivityResult(requestCode,resultCode,data);
}
在父fragment的onActivityResult方法中调用相应子fragment的onActivityResult方法
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println(resultCode + " father");
mFragmentChild.onActivityResult(requestCode,resultCode,data);
}
onActivityResult顺序:Activity -> 父fragment -> 子fragment
方式2:使用getParentFragment的startActivityForResult方法去启动
在父fragment的onActivityResult方法中调用相应子fragment的onActivityResult方法
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println(resultCode + " father");
mFragmentChild.onActivityResult(requestCode,resultCode,data);
}
onActivityResult顺序:父fragment -> 子fragment -> Activity
点击两次才触发点击事件
问题案例:使用TextView作为接收点击事件的控件
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
那么此时这样设置的结果是:
1 TextView 可点击
2 TextView 可获取焦点
3 TextView 可通过触摸获取焦点
问题:需要点击两次才能够触发点击事件
第一下点击:从其他地方获取焦点
第二次点击:触发点击事件
解决:
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="false"
或者
android:clickable="true"
android:focusable="false"
界面展开时会白一下问题
当Activity首次启动时应用界面会白的闪一下影响用户体验
可在style.xml文件中添加item设置默认的Window背景
- @drawable/bg
移除控件/按钮背景
移除控件背景(如:EditText)
android:background="@null"
移除按钮背景(如:RadioButton)
android:button="@null"
Android内存泄漏之Handle
/**
* 当Android应用程序启动时,会在主线程中创建一个Looper对象
* 该Looper对象存在于应用程序的整个生命周期中
* Looper对象实现了简单的消息队列,按一定的顺序发送消息
* 给Handle对象,由Handle对象进行消息处理
* 当创建Handle对象时会与Looper对象进行关联
* 在消息发送后,Message对象持有发送该消息的Handle对象
* 从而调用该Handle对象的handleMessage方法处理消息
*
* 如何造成内存泄漏?
* 首先,所有内部类(包括匿名内部类,不包括静态内部类)都会
* 持有其外部类的引用
* 此时外部类是Activity,那么就会造成内存泄漏
* Message对象持有Handle对象的引用
* 而Handle对象是一个匿名内部类对象
* 持有其外部类Activity的引用
* 此处延时20s发送了一个空消息
* 在Activity结束后消息仍然没有被处理完毕
* 此时Message对象持有Handle对象的引用
* 而Handle对象又持有Activity的引用
* 因此此时GC无法释放Activity对象,导致内存泄漏
*
* 如何避免?
* 1 使Handle对象不会持有外部类的引用 1:静态内部类 2:外部类
* 2 创建弱引用(使Handle对象不持有Activity的强引用) WeakReference
* 解析:使用静态内部类/外部类来自定义Handler派生类
* 这样不会导致Handle对象持有外部类对象的引用
* 但是静态内部类/外部类无法直接操作Activity中的变量
* 因此在实例化Handler对象时需要传入Activity对象以便在Handler中进行相关操作
* 此时虽然Handler对象不会持有外部类引用,但是将Activity对象作为参数传入后
* 仍然会导致Handler对象持有Activity对象的引用
* 那么此时就需要为Activity对象创建弱引用:GC对弱引用是采用急切回收方式
* 即当只有弱引用指向对象时,那么垃圾回收器会立即回收该对象
*
* 仍然存在的问题?
* 在Activity结束后,Handler 对象还在继续处理消息队列中的Message
* 正常在Activity结束后,消息已经没有处理的必要了
* 那么可以在onStop/onDestroy方法中移除消息对象
* removeMessages 移除Message
* removeCallbacks 移除Runnable
* removeCallbacksAndMessages 移除指定/所有(当参数为null时移除所有)
*/
public class MainActivity extends AppCompatActivity {
private final int WHAT_ONE = 0x3333;
private final int WHAT_TWO = 0x1233;
private final int WHAT_THREE = 0x9231;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler(this);
mHandler.sendEmptyMessageDelayed(WHAT_ONE, 20000);
mHandler.sendEmptyMessageDelayed(WHAT_TWO, 20000);
mHandler.sendEmptyMessageDelayed(WHAT_THREE, 20000);
this.finish();
}
// 使用静态内部类(相当于外部类):该类对象不会持有外部类的引用
private static class MyHandler extends Handler {
// 声明弱引用用于存放Activity
private WeakReference mReference;
public MyHandler(MainActivity activity) {
// 实例化弱引用对象
mReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 获取所引用的对象
if (mReference.get() != null) {
mReference.get().todo();
}
}
}
public void todo() {
System.out.println("todo something...");
}
@Override
protected void onDestroy() {
// 移除回调对象 Runnable接口对象
// mHandler.removeCallbacks(r);
// 移除消息对象 Message
mHandler.removeMessages(WHAT_ONE);
mHandler.removeMessages(WHAT_TWO);
mHandler.removeMessages(WHAT_THREE);
// 或者使用该方法移除指定/所有 (当参数为null时则移除所有)
// mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}