Android 中的内存泄漏

JVM垃圾回收机制 会对没有引用的对象进行回收,而持有引用的对象是不能被回收的。

匿名内部类包含如下特征
1. new 操作符
2. 实现一个接口,或者继承自一个类
3. 有构造函数的调用,接口因为没有构造函数,默认是无参构造函数。
4. 拥有body,就像类定义一样,body内可以声明方法,但是 不能拓展构造函数。

匿名内部内可以直接访问本地类的成员变量。可访问 方法内通过final 修饰的本地变量。
因此常见的泄漏就是匿名内部类存在对act的引用。

  private Object obj=new Object(){
        @Override
        public String toString() {
            return super.toString()+"bbb";
        }
    };
继续阅读“Android 中的内存泄漏”

老生常谈之Android 启动模式

关于启动模式网上一堆一堆的文章可以参考,我只是通过自己实践记录一下实验过程

Tip1. adb shell

dumpsys | grep "DUMP OF SERVICE"  //列举当前手机所有支持的dumpsys命令
查看activity 堆栈
dumpsys activity 包名

找到 Running activities (most recent first): 这一行下面的就是task信息

Task

  1. Task 是一个堆栈。里面维护了一系列Activity
  2. 不同应用的Activity 可以包含在一个Task 里边,比如系统发送邮件等等
继续阅读“老生常谈之Android 启动模式”

彻底弄清楚android 面试经典题:Handler 机制

向来觉得电话面试就觉得肯定黄,总有一种局促和不安,今天接到阿里的电话面,当时在家里。问的问题还是那几个老生常谈的问题。 view 的绘制啊, handler 机制啊,hashmap实现原理啊。效率啊,优化啊啥的。

今天可谓是都碰上了(其实还差那个asyntask 啊 http协议啊 多线程同步啊,jvm 啊,启动流程啊巴拉巴拉),这一关不过,下一关就没门啊,其实这个和项目经验或者一个人能不能做成事情并没有太大关系,最高的智商是把事做成,最高的情商是靠谱。

以前工作没那么多闲情看这个,那今天就带着面试的目的来彻底研究一下 面试第一题 Handler机制。

从应用开发这个角度来看,大家可能都知道 不能在子线程中直接new 一个hander。否则会抛出常见的

[code lang=text]
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
[/code]

然后大家也知道(上面明显提示),在子线程中假如要创建handler 可以先Looper.prepare()然后就可以创建了。

我们写一个常见的用handler 进行线程间通讯例子,子线程发送消息给主线程更新UI
继续阅读“彻底弄清楚android 面试经典题:Handler 机制”

Mac下线刷Android 设备

今天淘到一个二手nexus6p,因为考虑到安全问题,我需要刷回原生Android,我从官方网站上下载了刷机包
下载地址:
https://developer.android.com/preview/download-712.html

注意,以下线刷方法会清空所有数据,请提前备份。
其次要下载platform-tools 才能使用adb命令,因为我装了Android Studio,
sdk中包含 SDK manager 所以不需要额外下载。
如果你不是开发者,建议只需要下载platform-tools 即可 下载地址:
https://developer.android.com/studio/releases/platform-tools.html#tos-header
选择相应版本下载。
解压,切换到platform-tools 目录下:

让设备切换到bootloader模式下:有两种方式,任选其一

  • 运行
adb reboot bootloader
  • 关机,按「电源键+音量下」开机

手机进入reboot 模式后,其中有一行小字 好像是 the device is locked 或者device is unlocked ,如果是 locked 则
运行

fastboot flashing unlock

否则略过进入下一步,在此过程中,手机会显示一个确认的操作,请留意。
等待解锁之后,在mac上解压下载的压缩包,命令行切换到解压后的目录中,运行

./flash-all

回车,等待刷机成功吧。成功之后会重启手机。进行一些手机初始化设置之后,开启开发者模式,连接上电脑,重新执行

adb reboot bootloader

进入reboot模式,运行

fastboot flashing lock

重新锁定,大功告成。
Let’s enjoy the new Android!

利用python 构建一个APP 模拟测试环境

背景

做app开发的,和后台连调是一个很重要的工作。在我来到这家公司之前,一只以为所有的功能是后台先开发出来然后app才开始开发。但是由于现在app开发和后台开发是两套人马,所以不可避免的需要同时开发甚至同时上线。我们不得不面临的一个问题是后台只给出了接口,客户端并没有可以连调的环境,这样就不得不自己写虚拟数据模拟。让客户端能够按某个流程能够点下去。

继续阅读“利用python 构建一个APP 模拟测试环境”

利用java反射机制实现对象值拷贝

首先需求是这样的 ,我把它简化一下:

User user1=new User();
user1.setUserName("wxn");
User user2=new User();
user2.setUserName("kkkk");
User user3=user2;

接下来的操作我想让user2中的值和user1中的值全部相等。我们知道可以通过 user2=user1.clone()方法去获得一个新的对象,这种方法的详细介绍在这里可以学习。http://www.cnblogs.com/yxnchinahlj/archive/2010/09/20/1831615.html

但是这样clone出来的是一个新的对象,user2就是user1的一个新对象的拷贝,虽然user2中的值可以和user1中的值相等,但是 user3就已经不是user2之前的对象了,我需要user3中的值也跟着改变。

于是最简单的方法就是在User类中增加一个方法

public void copy(User user){
    this.setUserName(user.getUserName);
    this.setUserId(user.setUserId);
     .....
}

如果user中的属性太多怎么办?那写起来太麻烦了,除了方法名get和set有区别之外没什么区别, 于是我想起了之前看过的java反射机制来批量处理,顺便巩固一下java的反射机制,实现代码如下

public void copy(User user) {
//获得User类中所有的方法
		Method[] methods = user.getClass().getMethods();
		try {
		for (int i = 0; i < methods.length; i++) {
				String mName = methods[i].getName();
				if (mName.startsWith("set")) {
					String nName =null;
					//若为boolean的属性则没有get方法 只有 isXXX方法
					 if(boolean.class.equals(methods[i].getParameterTypes()[0]) ){
						  nName=mName.replace("set", "is");
					  }	else{
						  nName=mName.replace("set", "get");
					  }
					Method method = user.getClass().getDeclaredMethod(nName,
							null);
					methods[i].invoke(this, method.invoke(user, null));
				}

			} 
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

全部代码如下


import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 
 * @author ahsiu
 * 
 */

public class User {
	private int userId;
	private String userName;
	private String password;
	private String email;
	private String phone;
	private int loginState;
   private boolean flag;
	public int getUserId() {
		return userId;
	}

	public void copy(User user) {
		Method[] methods = user.getClass().getMethods();
		try {
			for (int i = 0; i < methods.length; i++) {
				String mName = methods[i].getName();
				if (mName.startsWith("set")) {

					String nName =null;
					//若为boolean的属性则没有get方法 只有 isXXX方法
					 if(boolean.class.equals(methods[i].getParameterTypes()[0]) ){
						  nName=mName.replace("set", "is");
					  }	else{
						  nName=mName.replace("set", "get");
					  }

					Method method = user.getClass().getDeclaredMethod(nName,
							null);
					methods[i].invoke(this, method.invoke(user, null));

				}

			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void setUserId(int userId) {
		this.userId = userId;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public int getLoginState() {
		return loginState;
	}

	public void setLoginState(int loginState) {
		this.loginState = loginState;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}

	public static void main(String[] args) {
		User user1=new User();
		user1.setUserName("wxn");
		User user2=new User();
		user2.setUserName("kkkk");
		User user3=user2;
		System.out.println(user1.getUserName());
		System.out.println(user2.getUserName());
		System.out.println(user3.getUserName());	
		System.out.println("");
		user2.copy(user1);
		System.out.println(user1.getUserName());
		System.out.println(user2.getUserName());
		System.out.println(user3.getUserName());
		System.out.println("");
		user3.setUserName("ahsiu");
		System.out.println(user1.getUserName());
		System.out.println(user2.getUserName());
		System.out.println(user3.getUserName());
	}
}

[转]android解决图片加载oom的方法

大家好,今天给大家分享的是解决解析图片的出现oom的问题,我们可以用BitmapFactory这里的各种Decode方法,如果图片很小的话,不会出现oom,但是当图片很大的时候

就要用BitmapFactory.Options这个东东了,Options里主要有两个参数比较重要.

options.inJustDecodeBounds = false/true;
//图片压缩比例.
options.inSampleSize = ssize;

我们去解析一个图片,如果太大,就会OOM,我们可以设置压缩比例inSampleSize,但是这个压缩比例设置多少就是个问题,所以我们解析图片可以分为俩个步骤,第一步就是获取图片的宽高,这里要设置Options.inJustDecodeBounds=true,这时候decode的bitmap为null,只是把图片的宽高放在Options里,然后第二步就是设置合适的压缩比例inSampleSize,这时候获得合适的Bitmap.这里我画了简单的流程图,如下:

继续阅读“[转]android解决图片加载oom的方法”

android 屏幕图标尺寸规范

1. 程序启动图标:

ldpi (120 dpi)
小屏

mdpi (160 dpi)
中屏

hdpi (240 dpi)
大屏

xhdpi (320 dpi)
特大屏

36 x 36 px

48 x 48 px

72 x 72 px

96 x 96 px

 

ICON圆角半径

iTunes Artwork icon ───────────────────────── 512px (90px)
App icon(iPhone4) ────────────────────────── 114px (20px)
App icon(iPad) ───────────────────────────── 72px (12px)
App icon(iPhone 3G/3GS) ───────────────────── 57px(10px)
Spotlight/Settings icon icon(iPhone4) ───────────── 58px (10px)
Spotlight/Settings icon icon(iPhone 3G/3GS/iPad) ──── 29px (9px)

 

android中的dialog

在Android开发中,我们经常需要在界面上弹出一些对话框,比如询问用户或者让用户选择。 下面总结了下dialog的一些样式。

 

1.该效果是当按返回按钮时弹出一个提示,来确保无误操作,采用常见的对话框样式。

创建dialog对话框方法代码如下:

  1. protected void dialog() {
  2.     AlertDialog.Builder builder = new Builder(Main.this);
  3.     builder.setMessage(“确认退出吗?”);
  4.     builder.setTitle(“提示”);
  5.     builder.setPositiveButton(“确认”, new OnClickListener() {
  6.      @Override
  7.      public void onClick(DialogInterface dialog, int which) {
  8.       dialog.dismiss();
  9.       Main.this.finish();
  10.      }
  11.     });
  12.     builder.setNegativeButton(“取消”, new OnClickListener() {
  13.      @Override
  14.      public void onClick(DialogInterface dialog, int which) {
  15.       dialog.dismiss();
  16.      }
  17.     });
  18.     builder.create().show();
  19.    }

在onKeyDown(int keyCode, KeyEvent event)方法中调用此方法

  1. public boolean onKeyDown(int keyCode, KeyEvent event) {
  2.     if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
  3.      dialog();
  4.     }
  5.     return false;
  6.    }

2.改变了对话框的图表,添加了三个按钮

创建dialog的方法代码如下:

  1. Dialog dialog = new AlertDialog.Builder(this).setIcon(
  2.        android.R.drawable.btn_star).setTitle(“喜好调查”).setMessage(
  3.        “你喜欢李连杰的电影吗?”).setPositiveButton(“很喜欢”,
  4.        new OnClickListener() {
  5.         @Override
  6.         public void onClick(DialogInterface dialog, int which) {
  7.          // TODO Auto-generated method stub
  8.          Toast.makeText(Main.this, “我很喜欢他的电影。”,
  9.            Toast.LENGTH_LONG).show();
  10.         }
  11.        }).setNegativeButton(“不喜欢”, new OnClickListener() {
  12.       @Override
  13.       public void onClick(DialogInterface dialog, int which) {
  14.        // TODO Auto-generated method stub
  15.        Toast.makeText(Main.this, “我不喜欢他的电影。”, Toast.LENGTH_LONG)
  16.          .show();
  17.       }
  18.      }).setNeutralButton(“一般”, new OnClickListener() {
  19.       @Override
  20.       public void onClick(DialogInterface dialog, int which) {
  21.        // TODO Auto-generated method stub
  22.        Toast.makeText(Main.this, “谈不上喜欢不喜欢。”, Toast.LENGTH_LONG)
  23.          .show();
  24.       }
  25.      }).create();
  26.      dialog.show();

3.信息内容是一个简单的View类型

创建dialog方法的代码如下:

  1. new AlertDialog.Builder(this).setTitle(“请输入”).setIcon(
  2.        android.R.drawable.ic_dialog_info).setView(
  3.        new EditText(this)).setPositiveButton(“确定”, null)
  4.        .setNegativeButton(“取消”, null).show();

4.信息内容是一组单选框

创建dialog方法的代码如下:

  1. new AlertDialog.Builder(this).setTitle(“单选框”).setIcon(
  2.        android.R.drawable.ic_dialog_info).setSingleChoiceItems(
  3.        new String[] { “Item1”, “Item2” }, 0,
  4.        new DialogInterface.OnClickListener() {
  5.         public void onClick(DialogInterface dialog, int which) {
  6.          dialog.dismiss();
  7.         }
  8.        }).setNegativeButton(“取消”, null).show();

 

5.信息内容是一组多选框

创建dialog方法的代码如下:

  1. new AlertDialog.Builder(this).setTitle(“复选框”).setMultiChoiceItems(
  2.        new String[] { “Item1”, “Item2” }, null, null)
  3.        .setPositiveButton(“确定”, null)
  4.        .setNegativeButton(“取消”, null).show();

6.信息内容是一组简单列表项

创建dialog的方法代码如下:

  1. new AlertDialog.Builder(this).setTitle(“列表框”).setItems(
  2.        new String[] { “Item1”, “Item2” }, null).setNegativeButton(
  3.        “确定”, null).show();

7.信息内容是一个自定义的布局

dialog布局文件代码如下:

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2.   <LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  3.    android:layout_height=”wrap_content” android:layout_width=”wrap_content”
  4.    android:background=”#ffffffff” android:orientation=”horizontal”
  5.    android:id=”@+id/dialog”>
  6.    <TextView android:layout_height=”wrap_content”
  7.      android:layout_width=”wrap_content”
  8.     android:id=”@+id/tvname” android:text=”姓名:” />
  9.    <EditText android:layout_height=”wrap_content”
  10.     android:layout_width=”wrap_content” android:id=”@+id/etname” android:minWidth=”100dip”/>
  11.   </LinearLayout>

创建dialog方法的代码如下:

  1. LayoutInflater inflater = getLayoutInflater();
  2.      View layout = inflater.inflate(R.layout.dialog,
  3.        (ViewGroup) findViewById(R.id.dialog));
  4.      new AlertDialog.Builder(this).setTitle(“自定义布局”).setView(layout)
  5.        .setPositiveButton(“确定”, null)
  6.        .setNegativeButton(“取消”, null).show();

 

好了,以上7种Android dialog对话框的使用方法就介绍到这里了,基本都全了,如果大家在android开发过程中遇到dialog的时候就可以拿出来看看。