Android 学习记录(持续更新)

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

 进入手机的超级终端Terminaladb 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{
            HashMap
  
  
   
    map = parseXml(is);
            version = map.get("version");
        }catch(Exception e){
            e.printStackTrace();
        }
        return version;
    }
    
    /**
     * 解析version.xml
     * 
   
   
    
    
     *  
    
    
     
     00.01.02.00
    
        
     * 
   
   
     */
    public static HashMap
   
   
    
     parseXml(InputStream is) throws Exception{
        HashMap
    
    
     
      map = 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 Map
    
    
     
      infos = 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发生时会转入该函数来处理
	 */
	@Override
	public 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() {
			@Override
			public 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.Entry
      
      
       
        entry : 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.jsp
      
    POST /android/uploadlog.jsp HTTP/1.1  
  Accept-Language: zh-cn 
  Host: 192.168.24.56
  Content-Type:multipart/form-data;boundary=7db372eb000e2
  User-Agent: WinHttpClient 
  Content-Length: 3693
  Connection: Keep-Alive
              //这里一定要有一行空行(\r\n  就是回车换行),来表明请求头字段结束
  --7db372eb000e2     //这是分界线,表示数据实体前面的“--”表示数据开始
  Content-Type: text/plain; charset=utf-8
  Content-Disposition: form-data; name="name"
  Content-Transfer-Encoding: 8bit
  
  my name is huyu     //这里就是name 对应的值
  --7db372eb000e2
  Content-Type: application/octet-stream; charset=utf-8
  Content-Disposition: form-data; name="file1"; filename="log1.log"
  
  (此处省略log1.log文件二进制数据...)
  --7db372eb000e2
  Content-Type: application/octet-stream; charset=utf-8
  Content-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, Map
       
       
        
         params, Map
        
        
          files){ 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 中动态权限申请:
 补充:
  • 用户选择允许后,会回调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.Creator
     
     
      
       CREATOR = 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 ArrayList
      
      
       
        queryData(String deviceType, String deviceMac, String startTime,
			String endTime, int pagecount, int pageIndex, boolean desc){
		ArrayList
       
       
        
         list = 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();
		ArrayList
         
         
          
           arg = 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); } } } } 
              
             
            
          
         
         
        
        
       
       
      
      




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值