示例代码下载http://download.csdn.net/detail/han1202012/8638801;

一. 崩溃日志本地存储

1. 保存原理解析

崩溃信息本地保存步骤 :

-- 1. 自定义类实现 UncaughtExceptionHandler : public class CrashHandler implements UncaughtExceptionHandler;

-- 2. 设置该自定义的 CrashHandler 类为单例模式 :

	// 单例模式
	private static CrashHandler INSTANCE = new CrashHandler();
	private CrashHandler() {
	}
	public static CrashHandler getInstance() {
		return INSTANCE;
	}

-- 重写 uncaughtException 方法

	@Override
	public void uncaughtException(Thread thread, Throwable ex)

-- 自定义 handleException 方法处理异常信息 : 在该方法中进行设备信息收集, 以及将信息保存到文件中;

(1) UncaughtExceptionHandler 类解析

UncaughtExceptionHandler 作用 : 该类处理以下情况, 如果有未捕获的异常发生, 出现了程序崩溃闪退的情况, 此时会回调该类的 uncaughtException 方法;

(2) 线程相关

线程相关 : 每个线程都对应有响应的默认的未捕获异常处理器;

-- 获取线程默认的未捕获异常处理器 : Thread.getDefaultUncaughtExceptionHandler();

-- 设置线程默认的未捕获异常处理器 : Thread.setDefaultUncaughtExceptionHandler(this);

(3) uncaughtException 方法


uncaughtException 方法解析 :

-- 回调时机 : 出现未定义的异常时;

-- 回调参数 : 回调时会传入 线程对象 和 要抛出的异常信息, 我们可以在程序中拿到这两个信息;

public void uncaughtException(Thread thread, Throwable ex)

(4) 手机设备信息

手机设备信息手机步骤 :

-- 1. 获取包信息 :

			//获取包管理器
			PackageManager pm = ctx.getPackageManager();
			//获取包信息
			PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
					PackageManager.GET_ACTIVITIES);

-- 2. 获取版本号信息

			if (pi != null) {
				//版本号
				String versionName = pi.versionName == null ? "null"
						: pi.versionName;
				//版本代码
				String versionCode = pi.versionCode + "";
				//将版本信息存放到 成员变量 Map<String, String> mInfos 中
				this.mInfos.put("versionName", versionName);
				this.mInfos.put("versionCode", versionCode);
			}

-- 3. 使用反射获取 Build 类成员变量变量 , 并遍历获取这些变量内容

		//获取 Build 中定义的变量, 使用反射方式获取, 该类中定义了设备相关的变量信息
		Field[] fields = Build.class.getDeclaredFields();
		//遍历获取额变量, 将这些信息存放到成员变量 Map<String, String> mInfos 中
		for (Field field : fields) {
			try {
				//设置 Build 成员变量可访问
				field.setAccessible(true);
				//将 设备相关的信息存放到 mInfos 成员变量中
				mInfos.put(field.getName(), field.get(null).toString());
				Log.d(TAG, field.getName() + " : " + field.get(null));
			} catch (Exception e) {
				Log.e(TAG, "an error occured when collect crash info", e);
			}
		}

(4) 保存崩溃信息到文件

保存文件步骤 : 这些步骤就很简单了, 使用 IO流即可;

-- 1. 将之前获取的 Build 设备信息, 版本信息, 崩溃信息转为字符串 :

		//存储相关的字符串信息
		StringBuffer sb = new StringBuffer();
		//将成员变量 Map<String, String> mInfos  中的数据 存储到 StringBuffer sb 中
		for (Map.Entry<String, String> entry : this.mInfos.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			sb.append(key + "=" + value + "\n");
		}

-- 2. 在 Logcat 中打印崩溃信息 : 之前的默认操作就是打印崩溃信息到 Logcat 中, 我们在这里继续执行完这个步骤, 否则Logcat 中没有数据的;

		//将 StringBuffer sb 中的字符串写出到文件中
		Writer writer = new StringWriter();
		PrintWriter printWriter = new PrintWriter(writer);
		ex.printStackTrace(printWriter);
		Throwable cause = ex.getCause();
		while (cause != null) {
			cause.printStackTrace(printWriter);
			cause = cause.getCause();
		}
		printWriter.close();

-- 3. 写出数据到文件中 : IO 流知识点, 不再做过多赘述;

		String result = writer.toString();
		sb.append(result);
		try {
			long timestamp = System.currentTimeMillis();
			String time = formatter.format(new Date());
			String fileName = "crash-" + time + "-" + timestamp + ".txt";
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				//获取文件输出路径
				String path = Environment.getExternalStorageDirectory()
						+ "/crashinfo/";
				//创建文件夹和文件
				File dir = new File(path);
				if (!dir.exists()) {
					dir.mkdirs();
				}
				//创建输出流
				FileOutputStream fos = new FileOutputStream(path + fileName);
				//向文件中写出数据
				fos.write(sb.toString().getBytes());
				fos.close();
			}
			return fileName;
		} catch (Exception e) {
			Log.e(TAG, "an error occured while writing file...", e);
		}

2. 代码及示例

(1) 相关代码示例

故意发生错误的代码 :

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

	}

	public void onClick(View view) {
		int i = 3;
		i = i / 0;
	}

}

CrashHandler 注册代码 : 在 Activity 或者 Application 中注册该代码;

CrashHandler.getInstance().init(getApplicationContext());

CrashHandler 代码 :

package cn.org.octpus.crash;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * UncaughtExceptionHanlder 作用 : 处理 线程被未捕获的异常终止 的情况, 一旦出现了未捕获异常崩溃, 系统就会回调该类的
 * uncaughtException 方法;
 */
public class CrashHandler implements UncaughtExceptionHandler {
	// 用于打印日志的 TAG 标识符
	public static final String TAG = "octopus.CrashHandler";

	// 系统默认的UncaughtException处理类
	private Thread.UncaughtExceptionHandler mDefaultHandler;
	// 程序的Context对象
	private Context mContext;
	// 用来存储设备信息和异常信息
	private Map<String, String> mInfos = new HashMap<String, String>();
	// 用于格式化日期,作为日志文件名的一部分
	private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

	// 单例模式
	private static CrashHandler INSTANCE = new CrashHandler();
	private CrashHandler() {
	}
	public static CrashHandler getInstance() {
		return INSTANCE;
	}

	/**
	 * 初始化该类, 向系统中注册
	 * @param context
	 */
	public void init(Context context) {
		mContext = context;
		// 获取系统默认的 UncaughtException 处理器
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
		// 设置该 CrashHandler 为程序的默认处理器
		Thread.setDefaultUncaughtExceptionHandler(this);
	}

	/*
	 * 出现未捕获的异常时, 会自动回调该方法
	 * (non-Javadoc)
	 * @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable)
	 */
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		/*
		 * 调用 handleException() 方法处理该线程
		 * 如果返回 true 说明处理成功, 如果返回 false 则调用默认的异常处理器来处理
		 * 一般情况下该方法都会成功处理
		 */
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果用户没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				Log.e(TAG, "error : ", e);
			}
			// 退出程序
			android.os.Process.killProcess(android.os.Process.myPid());
			System.exit(1);
		}
	}

	/**
	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
	 * @param ex
	 * 		异常信息
	 * @return
	 * 		true:如果处理了该异常信息;否则返回false.
	 */
	private boolean handleException(Throwable ex) {
		if (ex == null) {
			return false;
		}
		/*
		 * 使用Toast来显示异常信息,
		 * 由于在主线程会阻塞,
		 * 不能实时出现 Toast 信息,
		 * 这里我们在子线程中处理 Toast 信息
		 */
		new Thread() {
			@Override
			public void run() {
				Looper.prepare();
				Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG)
						.show();
				Looper.loop();
			}
		}.start();
		// 收集设备参数信息
		collectDeviceInfo(mContext);
		// 保存日志文件
		saveCrashInfo2File(ex);
		return true;
	}

	/**
	 * 收集设备参数信息, 将手机到的信息存储到
	 * @param ctx
	 * 		上下文对象
	 */
	public void collectDeviceInfo(Context ctx) {
		try {
			//获取包管理器
			PackageManager pm = ctx.getPackageManager();
			//获取包信息
			PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
					PackageManager.GET_ACTIVITIES);
			if (pi != null) {
				//版本号
				String versionName = pi.versionName == null ? "null"
						: pi.versionName;
				//版本代码
				String versionCode = pi.versionCode + "";
				//将版本信息存放到 成员变量 Map<String, String> mInfos 中
				this.mInfos.put("versionName", versionName);
				this.mInfos.put("versionCode", versionCode);
			}
		} catch (NameNotFoundException e) {
			Log.e(TAG, "an error occured when collect package info", e);
		}

		//获取 Build 中定义的变量, 使用反射方式获取, 该类中定义了设备相关的变量信息
		Field[] fields = Build.class.getDeclaredFields();
		//遍历获取额变量, 将这些信息存放到成员变量 Map<String, String> mInfos 中
		for (Field field : fields) {
			try {
				//设置 Build 成员变量可访问
				field.setAccessible(true);
				//将 设备相关的信息存放到 mInfos 成员变量中
				mInfos.put(field.getName(), field.get(null).toString());
				Log.d(TAG, field.getName() + " : " + field.get(null));
			} catch (Exception e) {
				Log.e(TAG, "an error occured when collect crash info", e);
			}
		}
	}

	/**
	 * 保存错误信息到文件中
	 * @param ex
	 * @return 返回文件名称,便于将文件传送到服务器
	 */
	private String saveCrashInfo2File(Throwable ex) {
		//存储相关的字符串信息
		StringBuffer sb = new StringBuffer();
		//将成员变量 Map<String, String> mInfos  中的数据 存储到 StringBuffer sb 中
		for (Map.Entry<String, String> entry : this.mInfos.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			sb.append(key + "=" + value + "\n");
		}

		//将 StringBuffer sb 中的字符串写出到文件中
		Writer writer = new StringWriter();
		PrintWriter printWriter = new PrintWriter(writer);
		ex.printStackTrace(printWriter);
		Throwable cause = ex.getCause();
		while (cause != null) {
			cause.printStackTrace(printWriter);
			cause = cause.getCause();
		}
		printWriter.close();
		String result = writer.toString();
		sb.append(result);
		try {
			long timestamp = System.currentTimeMillis();
			String time = formatter.format(new Date());
			String fileName = "crash-" + time + "-" + timestamp + ".txt";
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				//获取文件输出路径
				String path = Environment.getExternalStorageDirectory()
						+ "/crashinfo/";
				//创建文件夹和文件
				File dir = new File(path);
				if (!dir.exists()) {
					dir.mkdirs();
				}
				//创建输出流
				FileOutputStream fos = new FileOutputStream(path + fileName);
				//向文件中写出数据
				fos.write(sb.toString().getBytes());
				fos.close();
			}
			return fileName;
		} catch (Exception e) {
			Log.e(TAG, "an error occured while writing file...", e);
		}
		return null;
	}
}

(2) 结果示例

崩溃日志存放文件路径 : /storage/sdcard0/crashinfo/crash-2015-04-27-19-31-41-1430134301642.txt;

-- 说明 : 其中 /storage/sdcard0/ 是系统默认的 SD 卡路径, crashinfo/crash-2015-04-27-19-31-41-1430134301642.txt 是我们创建的文件;

崩溃日志内容 :

1430134301642.txt                                                             <
HARDWARE=pxa1088
RADIO=unknown
versionCode=1
HOST=SWDA2601
TAGS=release-keys
ID=JDQ39
MANUFACTURER=samsung
TYPE=user
IS_TRANSLATION_ASSISTANT_ENABLED=false
IS_SECURE=false
TIME=1416298944000
FINGERPRINT=samsung/wilcoxdszn/wilcoxds:4.2.2/JDQ39/G3812ZNUANK1:user/release-keys
UNKNOWN=unknown
BOARD=PXA1088
PRODUCT=wilcoxdszn
versionName=1.0
DISPLAY=JDQ39.G3812ZNUANK1
USER=se.infra
DEVICE=wilcoxds
MODEL=SM-G3812
BOOTLOADER=unknown
CPU_ABI=armeabi-v7a
CPU_ABI2=armeabi
IS_SYSTEM_SECURE=false
IS_DEBUGGABLE=false
SERIAL=5202889565301100
BRAND=samsung
java.lang.IllegalStateException: Could not execute method of the activity
	at android.view.View$1.onClick(View.java:3804)
	at android.view.View.performClick(View.java:4439)
	at android.widget.Button.performClick(Button.java:142)
	at android.view.View$PerformClick.run(View.java:18395)
	at android.os.Handler.handleCallback(Handler.java:725)
	at android.os.Handler.dispatchMessage(Handler.java:92)
	at android.os.Looper.loop(Looper.java:176)
	at android.app.ActivityThread.main(ActivityThread.java:5319)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
	at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at android.view.View$1.onClick(View.java:3799)
	... 12 more
Caused by: java.lang.ArithmeticException: divide by zero
	at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
	... 15 more
java.lang.reflect.InvocationTargetException
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at android.view.View$1.onClick(View.java:3799)
	at android.view.View.performClick(View.java:4439)
	at android.widget.Button.performClick(Button.java:142)
	at android.view.View$PerformClick.run(View.java:18395)
	at android.os.Handler.handleCallback(Handler.java:725)
	at android.os.Handler.dispatchMessage(Handler.java:92)
	at android.os.Looper.loop(Looper.java:176)
	at android.app.ActivityThread.main(ActivityThread.java:5319)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
	at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ArithmeticException: divide by zero
	at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
	... 15 more
java.lang.ArithmeticException: divide by zero
	at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at android.view.View$1.onClick(View.java:3799)
	at android.view.View.performClick(View.java:4439)
	at android.widget.Button.performClick(Button.java:142)
	at android.view.View$PerformClick.run(View.java:18395)
	at android.os.Handler.handleCallback(Handler.java:725)
	at android.os.Handler.dispatchMessage(Handler.java:92)
	at android.os.Looper.loop(Looper.java:176)
	at android.app.ActivityThread.main(ActivityThread.java:5319)
	at java.lang.reflect.Method.invokeNative(Native Method)
	at java.lang.reflect.Method.invoke(Method.java:511)
	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
	at dalvik.system.NativeStart.main(Native Method)

二. 崩溃日志保存到远程服务器

实现途径 :

-- 1. 自己开发服务器端 : 在上面的保存文件的基础上, 在开发一个服务器端, 使用网络编程接口将打印到文件的内容上传到服务器端;

-- 2. 使用第三方服务 : 使用 Testin 的崩溃大师, 地址 http://crash.testin.cn/app?scnavbar ;

1. 集成崩溃大师步骤

(1) 创建应用 获取 key 值

创建应用步骤 :

-- 1. 创建界面 http://crash.testin.cn/apm/task/create ;

-- 2. 填写一个应用信息 :

-- 3. 获取 key : 点击"提交并获取 Appkey" 按钮, 获取到了 AppKey "0da6263ca1f5b84a2dd405b07227f483";

(2) 设置类型

第二步设置类型, 选择默认的应用即可 :

(3) 下载 jar 包

jar 包简介 : 集成崩溃大师, 只需要集成一个 jar 包即可, 点击如下按钮即可下载 该 jar 包;

-- jar 包内容 : 下载后解压, jar 包名称是 testinagent.jar ;

(4) 导入 SDK

导入 SDK : 在应用下 创建一个 libs 目录, 将jar 包拷贝进去即可;

(5) 配置 AndroidManifest.xml 文件

在配置文件中添加如下用户权限即可 :

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.GET_TASKS" />

(6) 初始化设置

在代码中注册 :

		//初始化 Testin 崩溃大师
		TestinAgent.init(getApplicationContext(), "0da6263ca1f5b84a2dd405b07227f483", "octopus");

2. 测试示例

(1) 日志信息

测试成功会打印如下信息 :

04-27 20:11:40.890: E/TestinAgent(16432): ****************************************
04-27 20:11:40.890: E/TestinAgent(16432): A Java crash caught by TestinAgent, pkg=cn.org.octpus.crash
04-27 20:11:40.890: E/TestinAgent(16432): ----------------------------------------
04-27 20:11:40.890: E/TestinAgent(16432): reason: java.lang.IllegalStateException: Could not execute method of the activity
04-27 20:11:40.890: E/TestinAgent(16432): ----------------------------------------
04-27 20:11:40.890: E/TestinAgent(16432): stacktrace:
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.view.View$1.onClick(View.java:3804)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.view.View.performClick(View.java:4439)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.widget.Button.performClick(Button.java:142)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.view.View$PerformClick.run(View.java:18395)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.os.Handler.handleCallback(Handler.java:725)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.os.Handler.dispatchMessage(Handler.java:92)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.os.Looper.loop(Looper.java:176)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.app.ActivityThread.main(ActivityThread.java:5319)
04-27 20:11:40.890: E/TestinAgent(16432): 	at java.lang.reflect.Method.invokeNative(Native Method)
04-27 20:11:40.890: E/TestinAgent(16432): 	at java.lang.reflect.Method.invoke(Method.java:511)
04-27 20:11:40.890: E/TestinAgent(16432): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1102)
04-27 20:11:40.890: E/TestinAgent(16432): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:869)
04-27 20:11:40.890: E/TestinAgent(16432): 	at dalvik.system.NativeStart.main(Native Method)
04-27 20:11:40.890: E/TestinAgent(16432): Caused by: java.lang.reflect.InvocationTargetException
04-27 20:11:40.890: E/TestinAgent(16432): 	at java.lang.reflect.Method.invokeNative(Native Method)
04-27 20:11:40.890: E/TestinAgent(16432): 	at java.lang.reflect.Method.invoke(Method.java:511)
04-27 20:11:40.890: E/TestinAgent(16432): 	at android.view.View$1.onClick(View.java:3799)
04-27 20:11:40.890: E/TestinAgent(16432): 	... 12 more
04-27 20:11:40.890: E/TestinAgent(16432): Caused by: java.lang.ArithmeticException: divide by zero
04-27 20:11:40.890: E/TestinAgent(16432): 	at cn.org.octpus.crash.MainActivity.onClick(MainActivity.java:20)
04-27 20:11:40.890: E/TestinAgent(16432): 	... 15 more
04-27 20:11:40.890: E/TestinAgent(16432): ----------------------------------------
04-27 20:11:40.890: E/TestinAgent(16432): deviceinfo:
04-27 20:11:40.890: E/TestinAgent(16432): 	Device: samsung/SM-G3812
04-27 20:11:40.890: E/TestinAgent(16432): 	OS: 4.2.2
04-27 20:11:40.890: E/TestinAgent(16432): 	AppVersion: 1.0
04-27 20:11:40.890: E/TestinAgent(16432): 	PackageName: cn.org.octpus.crash
04-27 20:11:40.890: E/TestinAgent(16432): 	Activity: MainActivity
04-27 20:11:40.890: E/TestinAgent(16432): 	Total Disk Space: 2328 MB ; Free Disk Space: 1688 MB
04-27 20:11:40.890: E/TestinAgent(16432): 	Total SD Space: 2308 MB ; Free SD Space: 1668 MB
04-27 20:11:40.890: E/TestinAgent(16432): 	CPU Usage: 17.742 %
04-27 20:11:40.890: E/TestinAgent(16432): 	Memory Usage: 12 MB
04-27 20:11:40.890: E/TestinAgent(16432): 	Userinfo:
04-27 20:11:40.890: E/TestinAgent(16432): ****************************************

(2) 后台信息

后台崩溃数据 :

-- 控制台信息 : 进入崩溃大师的控制台, 就会看到刚看到的 CrashInfoDemo 应用, 此时我们估计产生的异常已经打印出来了;

-- 点击进入该应用的详情 :

-- 详细的崩溃信息查看 :

【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存的更多相关文章

  1. Android应用开发基础之一:数据存储和界面展现(一)

    Android项目的目录结构 Activity:应用被打开时显示的界面 src:项目代码 R.java:项目中所有资源文件的资源id Android.jar:Android的jar包,导入此包方可使用 ...

  2. Android应用开发基础之三:数据存储和界面展现(三)

    生成XML文件备份短信 创建几个虚拟的短信对象,存在list中 备份数据通常都是备份至sd卡 使用StringBuffer拼接字符串 把整个xml文件所有节点append到sb对象里 sb.appen ...

  3. 码农人生——从未学过Android如何开发Android App 案例讲解-第002期案例

    标题有点晃眼,本次分享是002期博文的实践故事,不会有任何代码.也不会教别人android 如何开发,类似博文已经有大批大批,而且还会有陆陆续续的人写,我写的文章,主要是经验之谈,希望总结出的一些方法 ...

  4. iOS开发-应用崩溃日志揭秘(一)

    作为一名应用开发者,你是否有过如下经历? 为确保你的应用正确无误,在将其提交到应用商店之前,你必定进行了大量的测试工作.它在你的设备上也运行得很好,但是,上了应用商店后,还是有用户抱怨会闪退 ! 如果 ...

  5. android studio 开发android app 真机调试

    大家都知道开发android app 的时候可以有2种调试方式, 一种是Android Virtual Device(虚拟模拟器) ,另一种就是真机调试. 这里要说的是真机调试的一些安装步骤: 1. ...

  6. HTML5移动开发实战必备知识——本地存储(1)

    本地缓存是HTML5出现的新技术,这个技术的出现使得移动web的开发成为了可能.我们都知道.要想打造一个高性能的移动应用,速度是关键.而在HTML5之前.仅仅有cookie可以存储数据,大小仅仅有4k ...

  7. iOS开发查看手机app本地存储的文件

    开发过程中,有时会在本地存储一些文件,但是我们不确定有没有存上,可以通过以下方法来查看测试手机上本地存储的文件: 1.选择xcode上面的window下面的Devices 2.先在左边选中你当前的设备 ...

  8. iOS开发--应用崩溃日志揭秘(二)

    场景 4: 吃棒棒糖时闪退! 用户邮件说, “当rage master吃棒棒糖时应用就闪退…” 另一用户说, “我让rage master 吃棒棒糖,没几次应用就闪退了!”崩溃日志如下: Incide ...

  9. HTML5移动开发实战必备知识——本地存储(2)

    了解了一些主要的本地存储使用方法和思想后.我们来系统的介绍一下本地存储. 本地存储分为三大类:localStorage/sessionStorage/本地数据库 localStorage和sessio ...

随机推荐

  1. 【完整项目】使用Scrapy模拟HTTP POST,获取完美名字

    1. 背景 最近有人委托我给小孩起个名字,说名字最好符合周易五行生克理论,然后给了我个网址,说像是这个网站中的八字测名,输入名字和生辰八字等信息,会给出来这个名字的分数和对未来人生的预测.当父母的自然 ...

  2. Linux之grep命令

    概述 所有的类linux系统都会提供一个名为grep(global regular expression print,全局正则表达式输出)的搜索工具.grep命令在对一个或多个文件的内容进行基于模式的 ...

  3. Springboot整合log4j2【详细步骤】

    1.去除logback中的依赖包 <dependency> <groupId>org.springframework.boot</groupId> <arti ...

  4. 如何查看cisco 生成树状态

    如何查看cisco 生成树状态  Role(角色): 1.Desg(指定端口)   2.Root(根端口)    3.Altn(替换端口) Sts(状态): 1.FWD(转发)      2.BLK( ...

  5. Linux 下查看tomcat版本

    1.echo $JAVA_HOME 2.进入jdk路径 3.java -version

  6. IOI2016Day2. paint

    题目链接:http://uoj.ac/problem/238 题目大意: 有一个长度为n的黑白序列,告诉你所以k个极长连续黑段长度和顺序.有一些位置的颜色已知,需要判断剩下未知的位置哪些颜色 一定是白 ...

  7. My Stuck in C++

    My Stuck in C++ Zhong-Liang Xiang Oct. 1st, 2017 这个专题记录了对于我而言, c++迷一样的东西.

  8. 关于html+ashx开发中几个问题的解决方法的感想和总结

    1.针对上篇文章中的服务端处理不敢苟同.仍然坚持使用反射,建立BaseHandler.ashx并在默认process方法中写上反射方法以及权限验证方法.针对具体的情况返回对应的值.服务端其他handl ...

  9. JAVA 面试基础

    经典的Java基础面试题________________________________________________________________________________________ ...

  10. 什么样的简历受HR青睐?

    简历是我们在求职过程中的名片,那么如何写出更容易受到HR青睐的简历呢? HR可能一天要看上百份的简历,他们都希望能够尽快筛选出合适的人,然后用更多的时间去跟候选人沟通.所以招聘人员一般看一份简历只会花 ...