Android 学习记录(持续更新)

1.AndroidManifest.xml 详解:

http://www.jb51.net/article/73731.htm

(AndroidManifest.xml 中不常用的设置属性)

http://www.cnblogs.com/cpacm/p/4083443.html

2.adb 常用命令:

http://blog.sina.com.cn/s/blog_851686e60101h6ng.html

安装软件:adb install c:\user\desktop\123.apk (文件名称.apk)

重新安装该软件:adb install -rc:\user\desktop\123.apk (文件名称.apk)

卸载apk软件:adb uninstall com.huyu.test (包名.apk)

查看手机上的运行日志,此项可以用来查错:adb logcat

查看手机是否连接,以及连接了几台手机:adb devices

A为手机路径,B为电脑路径,意思为:把文件从手机中复制到电脑上:adb pull A B

A为手机路径,B为电脑路径,意思为:把文件从电脑复制到手机上:adb push A B

操作以上命令前请执行:重新挂载文件系统:adb remount

进入手机的超级终端Terminal:adb shell

重启手机:adb reboot

重启到Recovery界面:adb reboot recovery

重启到bootloader界面:adb reboot bootloader

显示该目录下的文件和文件夹:ls

进入某个目录:cd(注意分隔斜杠方向是/)

获得最高权限:su

查找设备:adb wait-for-device

断开连接:adb kill-server

开始连接:adb start-server

3.RTL布局:

http://book.51cto.com/art/201311/418549.htm

4.安卓网络切换时多次广播解决办法:

在网络变化时,监听 ConnectivityManager.CONNECTIVITY_ACTION时会收到多次广播:

①.4G打开,wifi从关闭到打开:经历 wifi1连接(广播第一次)--4G关闭(广播第二次)--wifi1连上(广播第三次)

②.4G打开,wifi切换:经历 wifi1断开(广播第一次)--4G连上(广播第二次)--wifi2连上(广播第三次)

根据这种情况下,如果需要在网络切换时重新初始化TCP,那么可以这么写:

/**
* wifi切换广播会有多次,但我么一般只希望一次,呢么可以这么处理
*/
public class NetworkConnectChangedReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if(ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())){
//这个监听网络连接的设置,包括wifi和移动数据 的打开和关闭
NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (info != null) {
if(NetworkInfo.State.CONNECTED == info.getState()){
Log.i("TAG", "网络连上,类型 = "+info.getTypeName());
}else if(info.getType() == ConnectivityManager.TYPE_WIFI){
if(NetworkInfo.State.DISCONNECTING == info.getState()){
Log.i("TAG", "WIFI连上,但是wifi没网络");
}
}
}
}
}
}

5.安卓获取CPU版本:

 


6.安卓UI设计图(绝对好用)

 **在XML布局使用单位为sp,dp

 **在java代码中设置参数时,默认使用的是px,需要进行转换成dp

 **

public class DensityUtil {
/**
* 文字的尺寸一律用sp单位,非文字的尺寸一律使用dp单位。
* 例如textSize="16sp"、layout_width="60dp";
* 偶尔需要使用px单位,例如需要在屏幕上画一条细的分隔线时:
*
*/
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
// 先获取手机的像素密度
final float scale = context.getResources().getDisplayMetrics().density;
//+0.5f 是为了四舍五入
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
}

7.从src 目录下的 version.xml 文件读取信息

**

class MyUtils{/*** 从src 目录下的 version.xml 文件读取信息* */public static String getVersion(){String version = "";InputStream is = MyUtils.class.getClassLoader().getResourceAsStream("version.xml");try{HashMapmap = parseXml(is);version = map.get("version");}catch(Exception e){e.printStackTrace();}return version;}/*** 解析version.xml* *  00.01.02.00* */public static HashMapparseXml(InputStream is) throws Exception{HashMapmap = new HashMap();//实例化一个文档构建器工厂DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();//获取文档构建器DocumentBuilder builder = factory.newDocumentBuilder();//获取文档实例Document document  = builder.parse(is);//获取 XML根节点Element root = document.getDocumentElement();//获取子节点NodeList childNodes = root.getChildNodes();//遍历子节点for(int i = 0; i < childNodes.getLength(); i++){Node childNode = (Node) childNodes.item(i);if(childNode.getNodeType == Node.ELEMENT_NODE){Element childElement = (Element) childNode;//对比节点名字if("version".equals(childElement.getNodeName())){map.put("version", childElement.getFirstChild().getNodeValue());}}}return map;}}

8.Home键监听

**


private static HomeWatcherReceiver mHomeKeyReceiver = null;
private static void registerHomeKeyReceiver(Context context) {
Log.i(LOG_TAG, "registerHomeKeyReceiver");
mHomeKeyReceiver = new HomeWatcherReceiver();
final IntentFilter homeFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(mHomeKeyReceiver, homeFilter);
}
private static void unregisterHomeKeyReceiver(Context context) {
Log.i(LOG_TAG, "unregisterHomeKeyReceiver");
if (null != mHomeKeyReceiver) {
context.unregisterReceiver(mHomeKeyReceiver);
}
}
private BroadcastReceiver mHomeKeyEventReceiver = new BroadcastReceiver() {
private static final String SYSTEM_REASON = "reason";
private static final String SYSTEM_HOME_KEY = "homekey";
private static final String SYSTEM_HOME_KEY_LONG = "recentapps";
private static final String SYSTEM_HOME_KEY_GLOBAL = "globalactions";
private static final String SYSTEM_HOME_KEY_LOCK = "lock";
private static final String SYSTEM_HOME_KEY_ASSIST = "assist";
@Override
public void onReceive(Context context, Intent intent) {
//注意不同手机的按键不同,所以需要对不同理由做区分.
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_REASON);
if (TextUtils.equals(reason, SYSTEM_HOME_KEY)) {
//表示短按Home键,程序到了后台
Toast.makeText(getApplicationContext(), "home", 1).show();
}else if(TextUtils.equals(reason, SYSTEM_HOME_KEY_LONG)){
//表示长按Home键 或者 activity切换键,显示最近使用的程序列表
}else if (SYSTEM_HOME_KEY_LOCK.equals(reason)) {
// 锁屏
Log.i(LOG_TAG, "lock");
}else if (SYSTEM_HOME_KEY_ASSIST.equals(reason)) {
// samsung 长按Home键
Log.i(LOG_TAG, "assist");
}
}
}
};

9.将图片复制到系统相册

 **

/*** 将 sd卡目录下的 screenshot 目录下的图片复制到 系统相册* */
public static boolean copyImgToGallery(Context context, String fileName){//判断SD卡是否存在boolean sdCardExist = Environment.getExteralStroageState().equals(Environment.MEDIA_MOUNTED);if(!sdCardExist){return false;}if(fileName == null || fileName.length() <= 0){return false;}try{ContentValues values = new ContentValues();value.put("datetaken", new Date().toString());value.put("mime_type", "image/png");value.put("_data", fileName);ContentResolver cr = context.getContentResolver();cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);}catch(Exception e){e.printStackTrace();}//扫描文件MediaScannerConnection.scanFile(context, new String[]{Environment.getExteralStroageDirectory().getPath() + File.separator + "screenshot"}, null, null);return true;
}
10.Tomcat配置内存信息:
 http://elf8848.iteye.com/blog/378805
 文章中说的修改注册表项只针对Tomcat5,Tomcat8上修改可以直接在安装目录下有个修改器staruptw.exe,可直接修改,或者修改注册表,位置如下:(需新建三个键值对,参数值根据机器而定,我的配置如下)
11.Touch事件分发机制:
  说明:图片中第二点更正:
  如果是ViewGroup的onTouchEvent()消费了事件,则之后的系列事件将会跳过viewGroup的onInterceptTouchEvent(),直接由(activity)dispatch-->(viewgroup)dispatch-->(viewgroup)onTouch 消费。
  如果是view的onTouchEvent()消费了事件,则之后的系列事件还是会调用viewGroup的onInterceptTouchEvent();
  关于requestDisallowInterceptTouchEvent(true);可以参考这篇博文:
  http://blog.csdn.net/chaihuasong/article/details/17499799
12.安卓proguard混淆:
  推荐郭霖和阿良的这两篇文章,看后绝不后悔。
http://blog.csdn.net/dai_zhenliang/article/details/42423575
  http://blog.csdn.net/guolin_blog/article/details/50451259
  这里截取部分关键信息:
13.强大的SQLite数据库工具:Litepal
  用法参考:http://blog.csdn.net/guolin_blog/article/details/38556989
14.从API 9 开始,UI线程将不允许做耗时操作(访问网络和磁盘),这样设计为了提高用户体验,但我们可以改变策略使得,UI线程也可以做耗时操作:(但不建议这么做!)
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}

15.调用安卓系统自带的播放器遇到的坑:
//在调用安卓自带播放器或图库等自带应用时注意:
//以调用自带播放器播放视频文件为例:
String filePath = Environment.getExternalStorageDirectory() + "/video.mp4"
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(android.content.Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(new File(filepath)), "video/*");
//坑1:不要分开写,否则不成功
//  intent.setData(Uri.fromFile(new File(filepath)));
//  intent.setType("video/*");//坑2:可能部分手机上会报错:ActivityNotFoundException 是因为使用了 Uri.parse(filepath)
//  请换成 Uri.fromFile(new File(filepath))//坑3:可能部分手机上会报错:ActivityNotFoundException 是因为使用了 video/mp4
//  部分手机刷机时因为刷机包不全,导致刷机后有的播放器代码不全,有的视频格式不支持,打不开,请使用  video/*
startActivity(intent);

16.关于显示系统状态栏的坑:
  问题:之前我在集成第三方摄像头开发时,我从视频列表FragmentA跳转到单个视频播放界面,此时没有finish();进入播放界面后,每次在视频横竖屏切换的时候,都会发生很神奇的现象:FragmentA所在的Activity会销毁了重建. 分析了好久没找到答案,也试过在列表调到播放界面的时候finish(),但因为Fragment所在的Activity有很多其他工作,每次都重建很是影响性能。最后发现,之前做别的摄像头时,主题选的是 android:theme="@android:style/Theme.NoTitleBar.Fullscreen";而这个摄像头我用的是动态更改主题,参见代码
/**动态修改是否显示系统状态栏*/
private void setScreenFullNoTitle(boolean enable){
if(enable){
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setAttributes(lp);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}else{
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setAttributes(lp);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
}

在每次横屏的时候设为true,竖屏的时候设为false;这就导致了我之前的问题。
  究其原因:原来Activity的主题是默认主题,是显示系统状态栏的,在跳转之后并没有finish(),播放界面在横竖屏切换的时候动态更改了屏幕的显示尺寸(前者的显示尺寸不包括系统状态栏,后者在横屏的时候包括),这样导致了横竖屏的时候需要重新绘制界面,所以也就会使得原Activity重建了。
  解决方案:要么在跳转播放视频时finish();要么设置主题为全屏后,不调用上面的动态修改方法。

17.安卓APP崩溃处理:
package com.huyu.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;/*** UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.* * @author user* * 在自定义的Application中的onCreate()中初始化即可:* CrashHandler crashHandler = CrashHandler.getInstance();  crashHandler.init(getApplicationContext());注意添加写SD卡权限* */
public class CrashHandler implements UncaughtExceptionHandler {public static final String TAG = "CrashHandler";//系统默认的UncaughtException处理类 private Thread.UncaughtExceptionHandler mDefaultHandler;//CrashHandler实例private static CrashHandler INSTANCE = new CrashHandler();//程序的Context对象private Context mContext;//用来存储设备信息和异常信息private Mapinfos = new HashMap();//日志目录private String path ;//用于格式化日期,作为日志文件名的一部分private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");/** 保证只有一个CrashHandler实例 */private CrashHandler() {}/** 获取CrashHandler实例 ,单例模式 */public static CrashHandler getInstance() {return INSTANCE;}/*** 初始化* * @param context*/public void init(Context context) {mContext = context;path = Environment.getExternalStorageDirectory().getPath()+"/huyuLog/"+mContext.getPackageName()+"/CrashLog/"//获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();//设置该CrashHandler为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this);}/*** 当UncaughtException发生时会转入该函数来处理*/@Overridepublic void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {//如果用户没有处理则让系统默认的异常处理器来处理mDefaultHandler.uncaughtException(thread, ex);} else {try {Thread.sleep(3000);} catch (InterruptedException e) {Log.e(TAG, "出现未捕获的异常,未休眠2s,即将退出APP : ", 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来显示异常信息new Thread() {@Overridepublic void run() {Looper.prepare();Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();Looper.loop();}}.start();//收集设备参数信息 collectDeviceInfo(mContext);//保存日志文件 String filePath = saveCrashInfo2File(ex);//发送邮件String content = new StringBuilder().append("手机制造商:"+Build.MANUFACTURER+"\n").append("系统定制商:"+Build.BRAND+"\n").append("型号:"+Build.MODEL+"\n").append("CPU指令集:"+Build.CPU_ABI+"\n").append("SDK版本:"+Build.VERSION.SDK_INT).toString();MailManager.getInstance().sendMailWithFile(mContext.getString(R.string.app_name)+" ErrorLog", content, filePath);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 + "";infos.put("versionName", versionName);infos.put("versionCode", versionCode);}} catch (NameNotFoundException e) {Log.e(TAG, "收集APP版本信息时出现异常", e);}Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);infos.put(field.getName(), field.get(null).toString());Log.d(TAG, field.getName() + " : " + field.get(null));} catch (Exception e) {Log.e(TAG, "收集crash信息时出现异常", e);}}}/*** 保存错误信息到文件中* * @param ex* @return   返回文件名称,便于将文件传送到服务器*/private String saveCrashInfo2File(Throwable ex) {StringBuffer sb = new StringBuffer();for (Map.Entryentry : infos.entrySet()) {String key = entry.getKey();String value = entry.getValue();sb.append(key + "=" + value + "\n");}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 = "crashLog-" + time + "-" + timestamp + ".log";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {File dir = new File(path);if (!dir.exists()) {dir.mkdirs();}FileOutputStream fos = new FileOutputStream(path + fileName);fos.write(sb.toString().getBytes());fos.close();}return path+fileName;} catch (Exception e) {Log.e(TAG, "将crash信息写入文件时出现异常", e);}return null;}
}
package com.huyu.crash;import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;import android.util.Log;/*** 上传工具类*/
public class UploadFileUtils {private static final String TAG = "uploadFile";private static final int TIME_OUT = 10*1000;   //超时时间private static final String CHARSET = "utf-8"; //设置编码/**HTTP 报文格式(http://www.360doc.com/content/12/0612/14/8093902_217673378.shtml)url  http://121.40.251.38:8080/android/uploadlog.jspPOST /android/uploadlog.jsp HTTP/1.1  Accept-Language: zh-cn Host: 192.168.24.56Content-Type:multipart/form-data;boundary=7db372eb000e2User-Agent: WinHttpClient Content-Length: 3693Connection: Keep-Alive//这里一定要有一行空行(\r\n  就是回车换行),来表明请求头字段结束--7db372eb000e2     //这是分界线,表示数据实体前面的“--”表示数据开始Content-Type: text/plain; charset=utf-8Content-Disposition: form-data; name="name"Content-Transfer-Encoding: 8bitmy name is huyu     //这里就是name 对应的值--7db372eb000e2Content-Type: application/octet-stream; charset=utf-8Content-Disposition: form-data; name="file1"; filename="log1.log"(此处省略log1.log文件二进制数据...)--7db372eb000e2Content-Type: application/octet-stream; charset=utf-8Content-Disposition: form-data; name="file2"; filename="log2.log"(此处省略log2.log文件二进制数据...)--7db372eb000e2--   //请求数据结束的标识:一定是分界线以“--”结束*/ /** * android上传文件到服务器* @param params  需要上传的 字符串文本 参数   没有可为null* @param file  需要上传的文件 map*          key--文件名,与服务器端一致,服务端靠这个来取文件(在网页上 xxx就是这里的文件名)*          value--文件的绝对路径(/sdcard/huyu/123.log)* @param RequestURL  请求的rul* @return  返回响应的内容*/public static String uploadFile(String RequestURL, Mapparams, Mapfiles){ String result = null; String BOUNDARY = UUID.randomUUID().toString(); //边界标识 随机生成 String PREFIX = "--" ; //网页里,请求参数 -- 表示 String LINE_END = "\r\n"; // 回车换行 String CONTENT_TYPE = "multipart/form-data"; //内容类型 try { URL url = new URL(RequestURL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(TIME_OUT); conn.setConnectTimeout(TIME_OUT); conn.setDoInput(true); //允许输入流 conn.setDoOutput(true); //允许输出流 conn.setUseCaches(false); //不允许使用缓存 conn.setRequestMethod("POST"); //请求方式 conn.setRequestProperty("Charset", CHARSET); //设置编码 conn.setRequestProperty("connection", "keep-alive"); conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary=" + BOUNDARY); DataOutputStream dos = new DataOutputStream( conn.getOutputStream()); //1.首先上传文本类型参数,没有可忽略 if(null != params && params.size() > 0){ StringBuilder sb = new StringBuilder(); for (Map.Entry entry : params.entrySet()) { sb.append(PREFIX).append(BOUNDARY).append(LINEND); sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND); sb.append("Content-Type: text/plain; charset=" + CHARSET+LINEND); sb.append("Content-Transfer-Encoding: 8bit" + LINEND); sb.append(LINEND); sb.append(entry.getValue()); sb.append(LINEND); } dos.write(sb.toString().getBytes()); } //2.上传文件 if(files != null){ for (Map.Entry file: files.entrySet()) { StringBuffer sb1 = new StringBuffer(); sb1.append(PREFIX); sb1.append(BOUNDARY); sb1.append(LINE_END); /** * 这里重点注意: * name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件 * filename是文件的名字,包含后缀名的 比如:abc.png */ sb1.append("Content-Disposition: form-data; name=\"img\"; filename=\""+file.getName()+"\""+LINE_END); sb1.append("Content-Type: application/octet-stream; charset="+CHARSET+LINE_END); sb1.append(LINE_END); dos.write(sb1.toString().getBytes()); InputStream is = new FileInputStream(file); byte[] bytes = new byte[1024]; int len = 0; while((len=is.read(bytes))!=-1){ dos.write(bytes, 0, len); } is.close(); dos.write(LINE_END.getBytes()); } //请求结束标志 byte[] end_data = (PREFIX+BOUNDARY+PREFIX+LINE_END).getBytes(); dos.write(end_data); dos.flush(); /** * 获取响应码 200=成功 */ int res = conn.getResponseCode(); // Log.e(TAG, "response code:"+res); if(res==200){ Log.e(TAG, "request success"); InputStream input = conn.getInputStream(); StringBuffer sb2= new StringBuffer(); int ss ; while((ss=input.read())!=-1){ sb2.append((char)ss); } result = sb2.toString(); }else{ Log.e(TAG, "request error --"+res); } dos.close(); conn.disconnect(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } /** * 网络是否可用 * @param context * @return */ public boolean isNetworkAvailable(Context context) { ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] info = mgr.getAllNetworkInfo(); if (info != null) { for (int i = 0; i < info.length; i++) { if (info[i].getState() == NetworkInfo.State.CONNECTED) { return true; } } } return false; } /**模拟调用*/ private static void doUpload(Context context){ if(!isNetworkAvailable(context)){ return; } //上传文本参数 String name=URLEncoder.encode("admin","utf-8"); String pass=URLEncoder.encode("admin1234","utf-8"); Map params = new HashMap (); params.put("NAME", name); params.put("PASSWORD", pass); //上传文件 Map upfiles = new HashMap (); File file = new File("/sdcard/"); File[] files = file.listFiles(new FilenameFilter ( @Override public boolean accept(File dir, String filename) { return filename.endsWith(".log"); } )); for (File f: files) { upfiles.put(f.getName(), new File(f.getAbsolutePath())); } uploadFile("http://121.40.251.38:8080/android/uploadlog.jsp", params, upfiles); } } private long FILE_MAX_SIZE; private String FILE_SAVE_PATH; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestContext req = new ServletRequestContext(request); if(FileUpload.isMultipartContent(req)){ DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload fileUpload = new ServletFileUpload(factory); fileUpload.setFileSizeMax(FILE_MAX_SIZE); List items = new ArrayList(); try { items = fileUpload.parseRequest(req); } catch(Exception e){ e.printStackTrace(); } Iterator it = items.iterator(); while(it.hasNext()){ FileItem fileItem = (FileItem)it.next(); if(fileItem.isFormField()){ System.out.println(fileItem.getFieldName()+" "+fileItem.getName()+" "+new String(fileItem.getString().getBytes("ISO-8859-1"),"GBK")); } else { System.out.println(fileItem.getFieldName()+" "+fileItem.getName()+" "+ fileItem.isInMemory()+" "+fileItem.getContentType()+" "+fileItem.getSize()); if(fileItem.getName()!=null && fileItem.getSize()!=0){ File fullFile = new File(fileItem.getName()); File newFile = new File(FILE_SAVE_PATH+fullFile.getName()); try { fileItem.write(newFile); } catch(Exception e){ e.printStackTrace(); } } else { System.out.println("no file choosen or empty file"); } } } } } public void init() throw ServletException{ //读取在web.xml中配置的init-param FILE_SAVE_PATH = getInitParameter("file_save_path"); FILE_MAX_SIZE = Long.parseLong(getInitParameter("file_max_size")); } UploadServlet com.huyu.UploadServlet file_save_path F:\\log\\ file_max_size 1*1024*1024 UploadServlet /UploadServlet /** servlet配置 只有jsp页面,当然不用配置servlet。但是,只要有servlet存在,都要对存在的servlet进行配置,这时候分两种情况: 1) 当存在jsp页面向servlet提交数据时,我们对servlet的配置如下: servlet名称 servlet类 servlet名称 /url (其中servlet名称自定,保持上下一致。servlet类是自己写的那个servlet类,用包名.类名给出,而url是我们访问jsp页面的路径,他必须和jsp页面提交数据的action保持一致。) 2) 如果只存在servlet或者servlet向jsp页面传递参数,(没有jsp页面提交数据)我们这时配置servlet如下: servlet名称 servlet类 servlet名称 /url 其他的都一致,就是url有点不同,这时url可以为任意值,当我们访问这个servlet的时候,一定要按照(../工程名/url) 这个形式进行访问,否则会报错。 **/package com.jwkj.global; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Environment; import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.List; /** * 日志服务,日志默认会存储在SDcar里如果没有SDcard会存储在内存中的安装目录下面。 1.本服务默认在SDcard中每天生成一个日志文件, * 2.如果有SDCard的话会将之前内存中的文件拷贝到SDCard中 3.如果没有SDCard,在安装目录下只保存当前在写日志 * 4.SDcard的装载卸载动作会在步骤2,3中切换 5.SDcard中的日志文件只保存7天 * * @author Administrator * */ public class LogService extends Service { private static final String TAG = "LogService"; private static final int MEMORY_LOG_FILE_MAX_SIZE = 10 * 1024 * 1024; // 内存中日志文件最大值,10M private static final int MEMORY_LOG_FILE_MONITOR_INTERVAL = 10 * 60 * 1000; // 内存中的日志文件大小监控时间间隔,10分钟 private static final int SDCARD_LOG_FILE_SAVE_DAYS = 7; // sd卡中日志文件的最多保存天数 private String LOG_PATH_MEMORY_DIR; // 日志文件在内存中的路径(日志文件在安装目录中的路径) private String LOG_PATH_SDCARD_DIR; // 日志文件在sdcard中的路径 @SuppressWarnings("unused") private String LOG_SERVICE_LOG_PATH; // 本服务产生的日志,记录日志服务开启失败信息 private final int SDCARD_TYPE = 0; // 当前的日志记录类型为存储在SD卡下面 private final int MEMORY_TYPE = 1; // 当前的日志记录类型为存储在内存中 private int CURR_LOG_TYPE = SDCARD_TYPE; // 当前的日志记录类型 private String CURR_INSTALL_LOG_NAME; // 如果当前的日志写在内存中,记录当前的日志文件名称 private String logServiceLogName = "DailyLog.log";// 本服务输出的日志文件名称 private SimpleDateFormat myLogSdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); private OutputStreamWriter writer; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HHmmss");// 日志名称格式 private Process process; private WakeLock wakeLock; private SDStateMonitorReceiver sdStateReceiver; // SDcard状态监测 private LogTaskReceiver logTaskReceiver; /* * 是否正在监测日志文件大小; 如果当前日志记录在SDcard中则为false 如果当前日志记录在内存中则为true */ private boolean logSizeMoniting = false; private static String MONITOR_LOG_SIZE_ACTION = "MONITOR_LOG_SIZE"; // 日志文件监测action private static String SWITCH_LOG_FILE_ACTION = "SWITCH_LOG_FILE_ACTION"; // 切换日志文件action @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); init(); register(); deploySwitchLogFileTask(); new LogCollectorThread().start(); } private void init() { // SDCard/Android/data/包名/files/DailyLog LOG_PATH_MEMORY_DIR = getFilesDir().getAbsolutePath() + File.separator+ "DailyLog"; LOG_PATH_SDCARD_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "SecruiLog" + getPackageName() + File.separator + "DailyLog"; LOG_SERVICE_LOG_PATH = LOG_PATH_MEMORY_DIR + File.separator + logServiceLogName; createLogDir(); /* ****************************************************** * try { writer = new OutputStreamWriter(new FileOutputStream( * LOG_SERVICE_LOG_PATH, true)); } catch (FileNotFoundException e) { * Log.e(TAG, e.getMessage(), e); } * ***************************************************** */ PowerManager pm = (PowerManager) getApplicationContext() .getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); CURR_LOG_TYPE = getCurrLogType(); Log.i(TAG, "LogService onCreate"); } private void register() { IntentFilter sdCarMonitorFilter = new IntentFilter(); sdCarMonitorFilter.addAction(Intent.ACTION_MEDIA_MOUNTED); sdCarMonitorFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); sdCarMonitorFilter.addDataScheme("file"); sdStateReceiver = new SDStateMonitorReceiver(); registerReceiver(sdStateReceiver, sdCarMonitorFilter); IntentFilter logTaskFilter = new IntentFilter(); logTaskFilter.addAction(MONITOR_LOG_SIZE_ACTION); logTaskFilter.addAction(SWITCH_LOG_FILE_ACTION); logTaskReceiver = new LogTaskReceiver(); registerReceiver(logTaskReceiver, logTaskFilter); } /** * 获取当前应存储在内存中还是存储在SDCard中 * * @return */ public int getCurrLogType() { if (!Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { return MEMORY_TYPE; } else { return SDCARD_TYPE; } } /** * 部署日志切换任务,每天凌晨切换日志文件 */ private void deploySwitchLogFileTask() { Intent intent = new Intent(SWITCH_LOG_FILE_ACTION); PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, 0); Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); // 部署任务 AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, sender); recordLogServiceLog("deployNextTask succ,next task time is:" + myLogSdf.format(calendar.getTime())); } /** * 日志收集 1.清除日志缓存 2.杀死应用程序已开启的Logcat进程防止多个进程写入一个日志文件 3.开启日志收集进程 4.处理日志文件 移动 * OR 删除 */ class LogCollectorThread extends Thread { public LogCollectorThread() { super("LogCollectorThread"); Log.d(TAG, "LogCollectorThread is create"); } @Override public void run() { try { wakeLock.acquire(); // 唤醒手机 clearLogCache(); List orgProcessList = getAllProcess(); List processInfoList = getProcessInfoList(orgProcessList); killLogcatProc(processInfoList); createLogCollector(); Thread.sleep(1000);// 休眠,创建文件,然后处理文件,不然该文件还没创建,会影响文件删除 handleLog(); wakeLock.release(); // 释放 } catch (Exception e) { e.printStackTrace(); recordLogServiceLog(Log.getStackTraceString(e)); } } } /** * 每次记录日志之前先清除日志的缓存, 不然会在两个日志文件中记录重复的日志 */ private void clearLogCache() { Process proc = null; List commandList = new ArrayList (); commandList.add("logcat"); commandList.add("-c"); try { proc = Runtime.getRuntime().exec( commandList.toArray(new String[commandList.size()])); StreamConsumer errorGobbler = new StreamConsumer( proc.getErrorStream()); StreamConsumer outputGobbler = new StreamConsumer( proc.getInputStream()); errorGobbler.start(); outputGobbler.start(); if (proc.waitFor() != 0) { Log.e(TAG, " clearLogCache proc.waitFor() != 0"); recordLogServiceLog("clearLogCache clearLogCache proc.waitFor() != 0"); } } catch (Exception e) { Log.e(TAG, "clearLogCache failed", e); recordLogServiceLog("clearLogCache failed"); } finally { try { proc.destroy(); } catch (Exception e) { Log.e(TAG, "clearLogCache failed", e); recordLogServiceLog("clearLogCache failed"); } } } /** * 关闭由本程序开启的logcat进程: 根据用户名称杀死进程(如果是本程序进程开启的Logcat收集进程那么两者的USER一致) * 如果不关闭会有多个进程读取logcat日志缓存信息写入日志文件 * * @param allProcList * @return */ private void killLogcatProc(List allProcList) { if (process != null) { process.destroy(); } String packName = this.getPackageName(); String myUser = getAppUser(packName, allProcList); /* * recordLogServiceLog("app user is:"+myUser); * recordLogServiceLog("========================"); for (ProcessInfo * processInfo : allProcList) { * recordLogServiceLog(processInfo.toString()); } * recordLogServiceLog("========================"); */ for (ProcessInfo processInfo : allProcList) { if (processInfo.name.toLowerCase( getResources().getConfiguration().locale).equals("logcat") && processInfo.user.equals(myUser)) { android.os.Process.killProcess(Integer .parseInt(processInfo.pid)); // recordLogServiceLog("kill another logcat process success,the process info is:" // + processInfo); } } } /** * 获取本程序的用户名称 * * @param packName * @param allProcList * @return */ private String getAppUser(String packName, List allProcList) { for (ProcessInfo processInfo : allProcList) { if (processInfo.name.equals(packName)) { return processInfo.user; } } return null; } /** * 根据ps命令得到的内容获取PID,User,name等信息 * * @param orgProcessList * @return */ private List getProcessInfoList(List orgProcessList) { List procInfoList = new ArrayList (); for (int i = 1; i < orgProcessList.size(); i++) { String processInfo = orgProcessList.get(i); String[] proStr = processInfo.split(" "); // USER PID PPID VSIZE RSS WCHAN PC NAME // root 1 0 416 300 c00d4b28 0000cd5c S /init List orgInfo = new ArrayList (); for (String str : proStr) { if (!"".equals(str)) { orgInfo.add(str); } } if (orgInfo.size() == 9) { ProcessInfo pInfo = new ProcessInfo(); pInfo.user = orgInfo.get(0); pInfo.pid = orgInfo.get(1); pInfo.ppid = orgInfo.get(2); pInfo.name = orgInfo.get(8); procInfoList.add(pInfo); } } return procInfoList; } /** * 运行PS命令得到进程信息 * * @return USER PID PPID VSIZE RSS WCHAN PC NAME root 1 0 416 300 c00d4b28 * 0000cd5c S /init */ private List getAllProcess() { List orgProcList = new ArrayList (); Process proc = null; try { proc = Runtime.getRuntime().exec("ps"); StreamConsumer errorConsumer = new StreamConsumer( proc.getErrorStream()); StreamConsumer outputConsumer = new StreamConsumer( proc.getInputStream(), orgProcList); errorConsumer.start(); outputConsumer.start(); if (proc.waitFor() != 0) { Log.e(TAG, "getAllProcess proc.waitFor() != 0"); recordLogServiceLog("getAllProcess proc.waitFor() != 0"); } } catch (Exception e) { Log.e(TAG, "getAllProcess failed", e); recordLogServiceLog("getAllProcess failed"); } finally { try { proc.destroy(); } catch (Exception e) { Log.e(TAG, "getAllProcess failed", e); recordLogServiceLog("getAllProcess failed"); } } return orgProcList; } /** * 开始收集日志信息 */ public void createLogCollector() { String logFileName = sdf.format(new Date()) + ".log";// 日志文件名称 List commandList = new ArrayList (); commandList.add("logcat"); commandList.add("-f"); // commandList.add(LOG_PATH_INSTALL_DIR + File.separator + logFileName); commandList.add(getLogPath()); commandList.add("-v"); commandList.add("time"); commandList.add("*:V"); // commandList.add("*:E");// 过滤所有的错误信息 // 过滤指定TAG的信息 // commandList.add("MyAPP:V"); // commandList.add("*:S"); try { process = Runtime.getRuntime().exec( commandList.toArray(new String[commandList.size()])); recordLogServiceLog("start collecting the log,and log name is:" + logFileName); // process.waitFor(); } catch (Exception e) { Log.e(TAG, "CollectorThread == >" + e.getMessage(), e); recordLogServiceLog("CollectorThread == >" + e.getMessage()); } } /** * 根据当前的存储位置得到日志的绝对存储路径 * * @return */ public String getLogPath() { createLogDir(); String logFileName = sdf.format(new Date()) + ".log";// 日志文件名称 if (CURR_LOG_TYPE == MEMORY_TYPE) { CURR_INSTALL_LOG_NAME = logFileName; Log.d(TAG, "Log stored in memory, the path is:" + LOG_PATH_MEMORY_DIR + File.separator + logFileName); return LOG_PATH_MEMORY_DIR + File.separator + logFileName; } else { CURR_INSTALL_LOG_NAME = null; Log.d(TAG, "Log stored in SDcard, the path is:" + LOG_PATH_SDCARD_DIR + File.separator + logFileName); return LOG_PATH_SDCARD_DIR + File.separator + logFileName; } } /** * 处理日志文件 1.如果日志文件存储位置切换到内存中,删除除了正在写的日志文件 并且部署日志大小监控任务,控制日志大小不超过规定值 * 2.如果日志文件存储位置切换到SDCard中,删除7天之前的日志,移 动所有存储在内存中的日志到SDCard中,并将之前部署的日志大小 监控取消 */ public void handleLog() { if (CURR_LOG_TYPE == MEMORY_TYPE) { deployLogSizeMonitorTask(); deleteMemoryExpiredLog(); } else { moveLogfile(); cancelLogSizeMonitorTask(); deleteSDcardExpiredLog(); } } /** * 部署日志大小监控任务 */ private void deployLogSizeMonitorTask() { if (logSizeMoniting) { // 如果当前正在监控着,则不需要继续部署 return; } logSizeMoniting = true; Intent intent = new Intent(MONITOR_LOG_SIZE_ACTION); PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, 0); AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), MEMORY_LOG_FILE_MONITOR_INTERVAL, sender); Log.d(TAG, "deployLogSizeMonitorTask() succ !"); // recordLogServiceLog("deployLogSizeMonitorTask() succ ,start time is " // + calendar.getTime().toLocaleString()); } /** * 取消部署日志大小监控任务 */ private void cancelLogSizeMonitorTask() { logSizeMoniting = false; AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); Intent intent = new Intent(MONITOR_LOG_SIZE_ACTION); PendingIntent sender = PendingIntent.getBroadcast(this, 0, intent, 0); am.cancel(sender); Log.d(TAG, "canelLogSizeMonitorTask() succ"); } /** * 检查日志文件大小是否超过了规定大小 如果超过了重新开启一个日志收集进程 */ private void checkLogSize() { if (CURR_INSTALL_LOG_NAME != null && !"".equals(CURR_INSTALL_LOG_NAME)) { String path = LOG_PATH_MEMORY_DIR + File.separator + CURR_INSTALL_LOG_NAME; File file = new File(path); if (!file.exists()) { return; } Log.d(TAG, "checkLog() ==> The size of the log is too big?"); if (file.length() >= MEMORY_LOG_FILE_MAX_SIZE) { Log.d(TAG, "The log's size is too big!"); new LogCollectorThread().start(); } } } /** * 创建日志目录 */ private void createLogDir() { File file = new File(LOG_PATH_MEMORY_DIR); boolean mkOk; if (!file.isDirectory()) { mkOk = file.mkdirs(); if (!mkOk) { mkOk = file.mkdirs(); } } /* ************************************ * file = new File(LOG_SERVICE_LOG_PATH); if (!file.exists()) { try { * mkOk = file.createNewFile(); if (!mkOk) { file.createNewFile(); } } * catch (IOException e) { Log.e(TAG, e.getMessage(), e); } } * *********************************** */ if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { file = new File(LOG_PATH_SDCARD_DIR); if (!file.isDirectory()) { mkOk = file.mkdirs(); if (!mkOk) { recordLogServiceLog("move file failed,dir is not created succ"); return; } } } } /** * 将日志文件转移到SD卡下面 */ private void moveLogfile() { if (!Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { // recordLogServiceLog("move file failed, sd card does not mount"); return; } File file = new File(LOG_PATH_SDCARD_DIR); if (!file.isDirectory()) { boolean mkOk = file.mkdirs(); if (!mkOk) { // recordLogServiceLog("move file failed,dir is not created succ"); return; } } file = new File(LOG_PATH_MEMORY_DIR); if (file.isDirectory()) { File[] allFiles = file.listFiles(); for (File logFile : allFiles) { String fileName = logFile.getName(); if (logServiceLogName.equals(fileName)) { continue; } // String createDateInfo = // getFileNameWithoutExtension(fileName); boolean isSucc = copy(logFile, new File(LOG_PATH_SDCARD_DIR + File.separator + fileName)); if (isSucc) { logFile.delete(); // recordLogServiceLog("move file success,log name is:"+fileName); } } } } /** * 删除内存下过期的日志 */ private void deleteSDcardExpiredLog() { File file = new File(LOG_PATH_SDCARD_DIR); if (file.isDirectory()) { File[] allFiles = file.listFiles(); for (File logFile : allFiles) { String fileName = logFile.getName(); if (logServiceLogName.equals(fileName)) { continue; } String createDateInfo = getFileNameWithoutExtension(fileName); if (canDeleteSDLog(createDateInfo)) { logFile.delete(); Log.d(TAG, "delete expired log success,the log path is:" + logFile.getAbsolutePath()); } } } } /** * 判断sdcard上的日志文件是否可以删除 * * @param createDateStr * @return */ public boolean canDeleteSDLog(String createDateStr) { boolean canDel = false; Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DAY_OF_MONTH, -1 * SDCARD_LOG_FILE_SAVE_DAYS);// 删除7天之前日志 Date expiredDate = calendar.getTime(); try { Date createDate = sdf.parse(createDateStr); canDel = createDate.before(expiredDate); } catch (ParseException e) { Log.e(TAG, e.getMessage(), e); canDel = false; } return canDel; } /** * 删除内存中的过期日志,删除规则: 除了当前的日志和离当前时间最近的日志保存其他的都删除 */ private void deleteMemoryExpiredLog() { File file = new File(LOG_PATH_MEMORY_DIR); if (file.isDirectory()) { File[] allFiles = file.listFiles(); Arrays.sort(allFiles, new FileComparator()); for (int i = 0; i < allFiles.length - 2; i++) { // "-2"保存最近的两个日志文件 File _file = allFiles[i]; if (logServiceLogName.equals(_file.getName()) || _file.getName().equals(CURR_INSTALL_LOG_NAME)) { continue; } _file.delete(); Log.d(TAG, "delete expired log success,the log path is:" + _file.getAbsolutePath()); } } } /** * 拷贝文件 * * @param source * @param target * @return */ private boolean copy(File source, File target) { FileInputStream in = null; FileOutputStream out = null; try { if (!target.exists()) { boolean createSucc = target.createNewFile(); if (!createSucc) { return false; } } in = new FileInputStream(source); out = new FileOutputStream(target); byte[] buffer = new byte[8 * 1024]; int count; while ((count = in.read(buffer)) != -1) { out.write(buffer, 0, count); } return true; } catch (Exception e) { e.printStackTrace(); Log.e(TAG, e.getMessage(), e); recordLogServiceLog("copy file fail"); return false; } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage(), e); recordLogServiceLog("copy file fail"); return false; } } } /** * 记录日志服务的基本信息 防止日志服务有错,在LogCat日志中无法查找 此日志名称为Log.log * * @param msg */ private void recordLogServiceLog(String msg) { if (writer != null) { try { Date time = new Date(); writer.write(myLogSdf.format(time) + " : " + msg); writer.write("\n"); writer.flush(); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, e.getMessage(), e); } } } /** * 去除文件的扩展类型(.log) * * @param fileName * @return */ private String getFileNameWithoutExtension(String fileName) { return fileName.substring(0, fileName.indexOf(".")); } class ProcessInfo { public String user; public String pid; public String ppid; public String name; @Override public String toString() { String str = "user=" + user + " pid=" + pid + " ppid=" + ppid + " name=" + name; return str; } } class StreamConsumer extends Thread { InputStream is; List list; StreamConsumer(InputStream is) { this.is = is; } StreamConsumer(InputStream is, List list) { this.is = is; this.list = list; } @Override public void run() { try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null && line.contains(getPackageName())) { if (list != null) { list.add(line); } } } catch (IOException ioe) { ioe.printStackTrace(); } } } /** * 监控SD卡状态 * * @author Administrator * */ class SDStateMonitorReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_MEDIA_UNMOUNTED.equals(intent.getAction())) { // 存储卡被卸载 if (CURR_LOG_TYPE == SDCARD_TYPE) { Log.d(TAG, "SDcar is UNMOUNTED"); CURR_LOG_TYPE = MEMORY_TYPE; new LogCollectorThread().start(); } } else { // 存储卡被挂载 if (CURR_LOG_TYPE == MEMORY_TYPE) { Log.d(TAG, "SDcar is MOUNTED"); CURR_LOG_TYPE = SDCARD_TYPE; new LogCollectorThread().start(); } } } } /** * 日志任务接收 切换日志,监控日志大小 * * @author Administrator * */ class LogTaskReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (SWITCH_LOG_FILE_ACTION.equals(action)) { new LogCollectorThread().start(); } else if (MONITOR_LOG_SIZE_ACTION.equals(action)) { checkLogSize(); } } } class FileComparator implements Comparator { @Override public int compare(File file1, File file2) { if (logServiceLogName.equals(file1.getName())) { return -1; } else if (logServiceLogName.equals(file2.getName())) { return 1; } String createInfo1 = getFileNameWithoutExtension(file1.getName()); String createInfo2 = getFileNameWithoutExtension(file2.getName()); try { Date create1 = sdf.parse(createInfo1); Date create2 = sdf.parse(createInfo2); if (create1.before(create2)) { return -1; } else { return 1; } } catch (ParseException e) { return 0; } } } @Override public void onDestroy() { super.onDestroy(); recordLogServiceLog("LogService onDestroy"); if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } if (process != null) { process.destroy(); } unregisterReceiver(sdStateReceiver); unregisterReceiver(logTaskReceiver); } } 

18.Android 6.0 中动态权限申请:
 参考:Android 6.0权限动态申请
 补充:
  • 用户选择允许后,会回调onRequestPermissionsResult方法, 该方法可类似如下处理:

Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
doNext(requestCode,grantResults);
}
// 接着根据requestCode和grantResults(授权结果)做相应的后续处理:
private void doNext(int requestCode, int[] grantResults) {
if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
} else {
// Permission Denied
}
}
}

Fragment中运行时权限的特殊处理

  • 在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment的requestPermissions方法,否则会回调到Activity onRequestPermissionsResult

  • 如果在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方 法,onRequestPermissionsResult不会回调回来,建议使用getParentFragment().requestPermissions方法。这个方法会回调到父Fragment中的onRequestPermissionsResult,在回调中加入以下代码可以把回调透传到子Fragment:

Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
List<Fragment> fragments = getChildFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null) {
fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
}
}
}
}
19.实现parcelable接口:
public class DataEntity implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.CreatorCREATOR = new Parcelable.Creator() {
public DataEntity createFromParcel(Parcel in) {
return new DataEntity(in);
}
public DataEntity[] newArray(int size) {
return new DataEntity[size];
}
};
public DataEntity(Parcel in) {
mData = in.readInt();
}
/**省略get/set和构造方法*/
}

20.数据库工具类:(更强大的工具类参考:LitePal)


package com.secrui.db;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.secrui.entity.AlarmRecordEntity;
import com.secrui.sdk.SettingManager;
import com.utils.StringUtils;
public class AlarmRecordDAO{
//解决多线程并发
private AtomicInteger mOpenCounter = new AtomicInteger();
private static AlarmRecordDAO dao;
private MyDBHelper helper;
private SQLiteDatabase sdb;
private final String TABLENAME = "alarmLog";
private AlarmRecordDAO(Context context, String userOwnDBName){
if("".equals(userOwnDBName))
userOwnDBName = new SettingManager(context).getUserName();
this.helper = new MyDBHelper(context, userOwnDBName+".db", null, 1);
}
public static synchronized AlarmRecordDAO getInstance(Context context, String userOwnDBName){
if(dao == null)
dao = new AlarmRecordDAO(context, userOwnDBName);
return dao;
}
/**打开数据库*/
public synchronized SQLiteDatabase openDB(){
if (mOpenCounter.incrementAndGet() == 1) {
sdb = helper.getReadableDatabase();
}
return sdb;
}
/**关闭数据库*/
public synchronized void closeDB(){
if (mOpenCounter.decrementAndGet() == 0) {
sdb.close();
}
}
/**插入数据,返回-1 表示插入失败*/
public long insert(AlarmRecordEntity alarmLogEntity){
if(null == alarmLogEntity){
Log.i("TAG数据库", "插入数据失败,传入参数为null");
return -1;
}
SQLiteDatabase db = helper.getReadableDatabase();
ContentValues values = new ContentValues();
values.put("_deviceName", alarmLogEntity.getDeviceAlias());
values.put("_alarmTime", alarmLogEntity.getAlarmTime());
values.put("_alarmZone", alarmLogEntity.getAlarmZone());
values.put("_deviceType", alarmLogEntity.getDeviceType());
values.put("_deviceMac", alarmLogEntity.getDeviceMac());
values.put("_isRead", alarmLogEntity.getIsRead());
long id = db.insert(TABLENAME, null, values);
Log.i("TAG数据库", "插入数据成功");
return id;
}
/**删除指定时间(某一天时分秒,以后可扩展,将表分开,年,月,日,时分秒)的数据*/
public void delete(String alarmTime){
SQLiteDatabase db = helper.getReadableDatabase();
db.delete(TABLENAME, "where _alarmTime = ?", new String[]{alarmTime});
db.close();
Log.i("TAG数据库", "删除数据成功");
}
/**删除数据,保留最近插入的 amount 条数据*/
public void delete(int amount){
SQLiteDatabase db = helper.getReadableDatabase();
String sql = "delete from "+TABLENAME+" "
+ "where (select count(_alarmTime) from "+TABLENAME+" )> "+amount+""
+ " and _alarmTime in (select _alarmTime from "+TABLENAME+" order by _id desc"
+ " limit (select count(_alarmTime) from "+TABLENAME+") offset "+amount+" )";
db.execSQL(sql);
db.close();
//      LogUtils.i("TAG数据库", "成功删除"+amount+"条数据");
}
/**查询数据库总条目数*/
public int queryDataCount(){
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query(TABLENAME, null, null, null, null, null, null);
int count= cursor.getCount();
cursor.close();
db.close();
return count;
}
/**
* 查询分页数据
* @param deviceType 设备类型
* @param deviceMac 设备MAC
* @param startTime 开始时间
* @param endTime   结束时间
* @param pagecount 每页数据量
* @param pageIndex 第几页
* @param desc  是否降序(倒叙)
* @return
*/
public ArrayListqueryData(String deviceType, String deviceMac, String startTime,
String endTime, int pagecount, int pageIndex, boolean desc){
ArrayListlist = new ArrayList();
SQLiteDatabase db = helper.getReadableDatabase();
//      String sql = "select * from "+TABLENAME+" where _deviceType = "+deviceType+" and _deviceMac = "+ deviceMac
//              +" and _alarmTime between '" +startTime+"' and '"+endTime
//              +"' order by "+ (desc ? "_id desc":"_id asc")
//              +" limit "+pagecount+" offset "+ (pageIndex-1) * pagecount;
StringBuilder where = new StringBuilder();
ArrayListarg = new ArrayList(); if(null != deviceType){ where.append("_deviceType = ? and "); arg.add(deviceType); } if(null != deviceMac){ where.append("_deviceMac = ? and "); arg.add(deviceMac); } if(null != startTime && null != endTime){ where.append("_alarmTime between ? and ?"); arg.add(startTime); arg.add(endTime); }else if(null != startTime){ where.append("_alarmTime > ?"); arg.add(startTime); }else if(null != endTime){ where.append("_alarmTime < ?"); arg.add(endTime); } String clause = where.toString(); if(clause.endsWith("and ")) clause = clause.substring(0, clause.length()-4); String[] args = new String[arg.size()]; arg.toArray(args); Cursor cursor = db.query(TABLENAME, null, clause.equals("") ? null : clause, args.length == 0 ? null : args, null, null, desc ? "_id desc":"_id asc", (pageIndex-1) * pagecount +","+ pagecount); if(null == cursor){ return list; } while(cursor.moveToNext()){ long id = cursor.getInt(cursor.getColumnIndex("_id")); String deviceName = cursor.getString(cursor.getColumnIndex("_deviceName")); String alarmTime = cursor.getString(cursor.getColumnIndex("_alarmTime")); String alarmZone = cursor.getString(cursor.getColumnIndex("_alarmZone")); String type = cursor.getString(cursor.getColumnIndex("_deviceType")); String mac = cursor.getString(cursor.getColumnIndex("_deviceMac")); byte isRead = (byte) cursor.getInt(cursor.getColumnIndex("_isRead")); list.add(new AlarmRecordEntity(type, mac, deviceName, alarmZone, alarmTime, id, isRead)); } cursor.close(); db.close(); Log.i("TAG数据库", "查询数据成功"); return list; } /**查询最近 count 条数据(降序,最后添加的在前)*/ public ArrayList queryLaterData(int count){ ArrayList list = new ArrayList (); SQLiteDatabase db = helper.getReadableDatabase(); // String sql = "seclet * from "+TABLENAME+" order by _id desc limit 100 "; // Cursor cursor = db.rawQuery(sql, null); Cursor cursor = db.query(TABLENAME, null, null, null, null, null, "_id desc", ""+count); if(null == cursor){ return list; } while(cursor.moveToNext()){ long id = cursor.getInt(cursor.getColumnIndex("_id")); String deviceName = cursor.getString(cursor.getColumnIndex("_deviceName")); String alarmTime = cursor.getString(cursor.getColumnIndex("_alarmTime")); String alarmZone = cursor.getString(cursor.getColumnIndex("_alarmZone")); String deviceType = cursor.getString(cursor.getColumnIndex("_deviceType")); String deviceMac = cursor.getString(cursor.getColumnIndex("_deviceMac")); byte isRead = (byte) cursor.getInt(cursor.getColumnIndex("_isRead")); list.add(new AlarmRecordEntity(deviceType, deviceMac, deviceName, alarmZone, alarmTime, id, isRead)); } cursor.close(); db.close(); Log.i("TAG数据库", "查询数据成功"); return list; } /**可扩展查询某一天,某一月,某一年的数据*/ /**更新数据*/ public int update(AlarmRecordEntity alarmLogEntity){ if(null == alarmLogEntity){ Log.i("TAG数据库", "更新失败,传入参数为null"); return -1; } ContentValues values = new ContentValues(); values.put("_deviceName", alarmLogEntity.getDeviceAlias()); values.put("_alarmTime", alarmLogEntity.getAlarmTime()); values.put("_alarmZone", alarmLogEntity.getAlarmZone()); values.put("_deviceType", alarmLogEntity.getDeviceType()); values.put("_isRead", alarmLogEntity.getIsRead()); SQLiteDatabase db = helper.getReadableDatabase(); int row = db.update(TABLENAME, values, "_deviceMac = ?", new String[]{alarmLogEntity.getDeviceMac()}); db.close(); Log.i("TAG数据库", "更新数据成功"); return row; } /**更新设备名字*/ public int updateDeviceName(String mac, String alias){ if(StringUtils.isEmpty(mac) || StringUtils.isEmpty(alias)){ Log.i("TAG数据库", "更新失败,传入参数为null或者空"); return -1; } ContentValues values = new ContentValues(); values.put("_deviceName", alias); SQLiteDatabase db = helper.getReadableDatabase(); int row = db.update(TABLENAME, values, "_deviceMac = ?", new String[]{mac}); db.close(); Log.i("TAG数据库", "更新设备名字成功,更改行数="+row); return row; } /**删除整个数据库*/ public void drop(){ SQLiteDatabase db = helper.getReadableDatabase(); String sql = "drop table if exists alarmLog"; db.execSQL(sql); db.close(); } class MyDBHelper extends SQLiteOpenHelper{ public MyDBHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } // 该方法在创建数据库的时候执行,一般会在此方法中执行一些建表的操作 @Override public void onCreate(SQLiteDatabase db) { String sql = "create table if not exists alarmLog (_id integer primary key autoincrement," + " _alarmTime text not null, _deviceName text not null, _alarmZone text not null," + " _deviceType text not null, _deviceMac text not null, _isRead integer not null )"; db.execSQL(sql); Log.i("TAG_DB", "alarmRecord 创建成功"); } // 该方法在数据库版本升级的时候调用,一般用来删除表格,更新版本,重建表格 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if(newVersion > oldVersion){ String sql="drop table if exists alarmLog"; db.execSQL(sql); Log.i("TAG_DB", "alarmRecord 删除成功"); onCreate(db); } } } } 

Android 学习记录(持续更新)相关推荐

  1. Android 学习资料(持续更新)

    概述 收集整理这份资料灵感来自于 trip_to_iOS, 征得同意引用了该资料的开头描述 收集整理这份资料主要帮助初学者学习 Android 开发, 希望能快速帮助到他们快速入门, 找到适合自己学习 ...

  2. R语言绘图、数据处理学习记录持续更新

    目录 20220411--基础知识学习 20220412--读写操作和基本函数 20220415--循环语句学习 20220418--数据框的操作 20220419--可视化练习 20230107-- ...

  3. Python学习记录——持续更新

    python获取当前日期 time.strftime('%Y-%m-%d') python通过命令行传参在py文件中如何读取 通过sys模块中的 sys.argv可以访问到所有的命令行参数,返回值是包 ...

  4. 【python爬虫学习记录 持续更新】多线程多进程,带线程池爬取实例

    文章目录 简介 多线程codingFrame 多进程codingFrame 线程池与进程池 线程池爬取实例(主页url隐了 主要看思路 和如何使用线程池框架) 简介 进程是资源单位 线程是执行单位 每 ...

  5. 重拾CCNA,学习笔记持续更新ing......(4)

    重拾CCNA,学习笔记持续更新ing......(4) 路由器作用功能的经典解说(笑)(非原创) 假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大 ...

  6. (一)Git学习记录(不断更新)

    作为程序员如果你还不知道 Git 和 GitHub,说不过去吧,赶紧来学习一波. 一.认识GitHub Git 是个版本控制系统,说明白点就是进行代码的各种管理,比如你写错代码进行回滚啊.追寻 Bug ...

  7. 达梦数据库操作记录_达梦数据库常用功能及命令记录 -- 持续更新

    达梦数据库常用功能及命令记录 -- 持续更新 达梦数据库常用功能及命令记录 达梦数据库语句的使用总体来说跟 oracle 很接近的, 这篇文章主要是把常用的情况和语句做了记录, 并且后续还会不断的持续 ...

  8. Android学习记录(一)

    文章目录 Android学习记录(1) 1.Android的相关介绍 1.1Android是什么 1.2使用工具(Android Studio) 1.3Android应用目录结构 2.Android案 ...

  9. 人生最好的php,mysql,linux,redis,docker等相关技术经典面试题,新手收藏学习,持续更新中。。。

    php面试题 1.写出你能想到的所有HTTP返回状态值,并说明用途(比如:返回404表示找不到页面) # 200:服务器请求成功 # 301:永久重定向,旧网页已被新网页永久替代 # 302:表示临时 ...

最新文章

  1. 计算机组装与维护实例教程,计算机组装与维护案例教学-20210714101609.pdf-原创力文档...
  2. App开发流程之右滑返回手势功能续
  3. 第二个例子:单链表实现基排序(桶排序)
  4. 201.09.22 除虫药水(线性dp)
  5. Mysql 主外键与索引之间的区别和联系
  6. IDEA配置java《算法》第四版环境(耗时6小时,总算配置成功了,希望能给大家一点帮助)
  7. 本地消息表实现机制讲解
  8. Valid Number
  9. Taro+react开发(85):taro路由参数
  10. LeetCode 1813. 句子相似性 III
  11. Python黑帽编程2.8 套接字编程
  12. wordpress-黑格网址blackgrid导航主题模板
  13. (十)java多线程之CountDownLatch
  14. JNI中访问JList的代码
  15. openCV,C++接口,cv::Mat矩阵数据元素读取
  16. 手机号码测凶吉附带手机号码归属地C#版
  17. Oracle数据库有哪些应用结构?
  18. 网络安全——Webshell管理工具
  19. 转《魏炜的举世无双大博客》
  20. java学习笔记——springmvc 之 数据自定义转换器 数据格式化 JSR303数据校验返回与接收JSON(@RequestBody 和 @ResponseBody)

热门文章

  1. uni-app 介绍,什么是uni-app,它是干嘛的
  2. Spring Boot事务
  3. 今日互联网关注(写在清明节后):每天都有值得关注的大变化
  4. Git出现Failed to connect to github.com port 443:Operation timed out问题解决
  5. MATLAB三维绘图(三)绘制等值线图
  6. Opencv中的erode和dilate(腐蚀和膨胀-python实现)
  7. transform(转换)
  8. 简易音乐播放器(Android Studio)
  9. 微信小程序商城 (后台JAVA)
  10. sysvinit源码分析 Linux-init-process-analyse