Android中ContentProvider 数据共享实现方法

 更新时间:2016年9月20日 20:01  点击:1979
通常在android应用中,数据都是在本应用沙盒之内的,其他外部应用不能够访问,那么如果一个应用需要访问另外一个应用的数据,怎么办呢?那就把另外一个应用的数据公布出来,比如android中的通讯录数据,这些数据是以ContentProvider方式提供与其他应用访问的。

那么我们也可以定义自己的ContentProvider来使跨应用共享数据。数据具体的存贮方式可以为数据库、文件,持久化或非持久化存储的其他形式。在这里我们还是使用sqlite数据库存贮数据吧。

老规矩,先来点基础知识。

一.基础知识

1:URI是什么?统一资源标识符,用来标识某一资源的。

通常一个Uri主要由以三部分组成:scheme、Authority、path
1.scheme:ContentProvider(内容提供者)的scheme已经由Android系统规定为:content://
2.主机名(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
要操作persion表中id为5的记录,可以构建这样的路径:/persion/5
要操作persion表中id为20的记录的name字段, persion/name/10
要操作persion表中的所有记录,可以构建这样的路径:/persion
使用Uri类中的parse()方法获取Uri: Uri uri = Uri.parse("content://com.dongzi/persion")

上面Uri的scheme: content://

Authority: com.dongzi

path: /contact

 


2、UriMatcher、ContentUrist和ContentResolver

Android系统提供了两个用于操作Uri的工具类:UriMatcher 和ContentUris

UriMatcher:用于匹配Uri:

 代码如下 复制代码
static final int CODES=2;
    static final int CODE=1;
    static final String AUTHORITY="com.dongzi";          //授权
    static final UriMatcher uriMatcher;                  //Uri匹配
    static {                                             //注册匹配的Uri以及返回码
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);  //不匹配任何路径返回-1
        uriMatcher.addURI(AUTHORITY, "persion", CODES);  //匹配content://com.dongzi/persion 返回2
        uriMatcher.addURI(AUTHORITY, "persion/#", CODE); //匹配content://com.dongzi/persion/1234 返回1
    }

    

ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
• withAppendedId(uri, id)用于为路径加上ID部分
• parseId(uri)方法用于从路径中获取ID部分

 代码如下 复制代码
//为Uri添加ID
        Uri uri=Uri.parse("content://"+AUTHORITY+"/persion");
        ContentUris.withAppendedId(uri, 1234);
        //生成后的Uri为:content://com.dongzi/person/1234
       
        //获取Uri后面的ID
        long id=ContentUris.parseId(Uri.parse("content://com.dongzi/person/1234"));
        //得到ID为:1234

ContentResolver提供了如下主要方法:

 代码如下 复制代码

@Override
    public int delete(Uri arg0, String arg1, String[] arg2) {
        //该方法用于供外部应用从ContentProvider删除数据。
        return 0;
    }
    @Override
    public String getType(Uri uri) {
        //该方法用于返回当前Url所代表数据的MIME类型。
        return null;
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //该方法用于供外部应用往ContentProvider添加数据。
        return null;
    }
    @Override
    public boolean onCreate() {
        //在其它应用第一次访问它时被创建。
        return false;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
         //该方法用于供外部应用从ContentProvider中获取数据。
        return null;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        //该方法用于供外部应用更新ContentProvider中的数据。
        return 0;
    }

这里主要说下Url所代表数据的MIME类型:

如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,

例如:要得到所有person记录的Uri为content://com.dongzi/person,那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。

如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,

例如:得到id为1234的person记录,Uri为content://com.dongzi/person/1234,那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"。

使用ContentResolver操作ContentProvider中的数据

当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查 询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。ContentResolver提供了ContentProvider对应的增、删、改、查方法。

监听ContentProvider中数据的变化

如果我们需要得到数据变化通知,可以使用ContentObserver对数据(数据采用uri描述)进行通知更改以及监听。

 代码如下 复制代码

//通知内容以及发生改变,同时注册了内容监听,监听到内容变化,就调用onChange方法
        this.getContext().getContentResolver().notifyChange(uri, new ContentObserver(new Handler()){
            public void onChange(boolean selfChange) {
                  //此处可以进行相应的业务处理
               }
        });

二.实战

说了那么多,是时候了解ContentProvider的使用了,我们这里采用sqlite存贮数据。当然,我们直接采用联系人数据不是更好?


1:首先我们定义DBHelper继承SQLiteOpenHelper,并建表。

 代码如下 复制代码

package com.dongzi;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

    static final String DB_NAME = "dongzi.db";
    static final int DB_VERSION = 1;
    static final String TABLE="persion";
    static final String TABLE_COLUMN_NAME="name";
    static final String TABLE_COLUMN_PHONE="phone";
    static final String CREATE_TABLE = "create table persion(id integer primary key autoincrement,name varchar(40) phone varchar(40))";
    static final String DRPO_TABLE="drop table if exists persion";
    public DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
     //这里其实是比较版本,然后升级数据库的,比如说是增加一个字段,或者删除一个字段,或者增加表
     db.execSQL(DRPO_TABLE);
     onCreate(db);
    }

}

2:然后定义MyContentProvider继承ContentProvider,并且在类加载时候初始化UriMatcher匹配,以及授权AUTHORITY,同时,这个ContentProvider需要在AndroidManifest.xml中进行注册,并添加授权。

代码如下:

 代码如下 复制代码

package com.dongzi;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;

public class MyContentProvider extends ContentProvider {
   
    DBHelper dbHelper=null;
    //MIME类型
    static final String PERSIONS_TYPE="vnd.android.cursor.dir/person";
    static final String PERSION_ITEM_TYPE="vnd.android.cursor.item/person";
    //返回码
    static final int CODES=2;
    static final int CODE=1;
    //授权
    static final String AUTHORITY="com.dongzi";          //授权
    static final UriMatcher uriMatcher;                  //Uri匹配
    static {                                             //注册匹配的Uri以及返回码
        uriMatcher=new UriMatcher(UriMatcher.NO_MATCH);  //不匹配任何路径返回-1
        uriMatcher.addURI(AUTHORITY, "persion", CODES);  //匹配content://com.dongzi/persion 返回2
        uriMatcher.addURI(AUTHORITY, "persion/#", CODE); //匹配content://com.dongzi/persion/1234 返回1
    }
   
    private void init(){
        //为Uri添加ID
        Uri uri=Uri.parse("content://"+AUTHORITY+"/persion");
        ContentUris.withAppendedId(uri, 1234);
        //生成后的Uri为:content://com.dongzi/person/1234
       
        //获取Uri后面的ID
        long id=ContentUris.parseId(Uri.parse("content://com.dongzi/person/1234"));
        //得到ID为:1234
    }
   
   
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        //该方法用于供外部应用从ContentProvider删除数据。
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        int count=0;
        switch(uriMatcher.match(uri)){
        case CODES:
          count = db.delete(DBHelper.DB_NAME, selection, selectionArgs);
          break;
        case CODE:
            // 下面的方法用于从URI中解析出id,对这样的路径content://com.dongzi/persion/1234
            // 进行解析,返回值为10
            long id = ContentUris.parseId(uri);
            String where = "id=" + id;// 删除指定id的记录
            where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上
            count = db.delete(DBHelper.DB_NAME, where, selectionArgs);
            break;
        default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
       
        }
        db.close();
        return count;
    }
    @Override
    public String getType(Uri uri) {
        //该方法用于返回当前Url所代表数据的MIME类型。
        switch(uriMatcher.match(uri)){
        case CODES:
         return PERSIONS_TYPE;               //这里CODES代表集合,故返回的是集合类型的MIME
        case CODE:
             return PERSION_ITEM_TYPE;
        default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
        }
    }
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //该方法用于供外部应用往ContentProvider添加数据。
        SQLiteDatabase db= dbHelper.getWritableDatabase();
        long id=0;
        //匹配Uri
        switch(uriMatcher.match(uri)){
         //返回码
        case CODES:
            id=db.insert(DBHelper.TABLE, DBHelper.TABLE_COLUMN_NAME, values);// 返回的是记录的行号,主键为int,实际上就是主键值
            return ContentUris.withAppendedId(uri, id);
        case CODE:
            id=db.insert(DBHelper.TABLE, DBHelper.TABLE_COLUMN_NAME, values);// 返回的是记录的行号,主键为int,实际上就是主键值
            String path = uri.toString();
            return Uri.parse(path.substring(0, path.lastIndexOf("/"))+id); // 替换掉id
        default:throw new IllegalArgumentException("throw Uri:"+uri.toString());
        }
    }
    @Override
    public boolean onCreate() {
        //在其它应用第一次访问它时被创建。
        dbHelper =new DBHelper(getContext());
        return false;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
         //该方法用于供外部应用从ContentProvider中获取数据。
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        Cursor cursor=null;
        switch(uriMatcher.match(uri)){
        case CODES:
            cursor=db.query(DBHelper.DB_NAME, projection, selection, selectionArgs, null, null, sortOrder);
            break;
        case CODE:
               //下面的方法用于从URI中解析出id,对这样的路径content://com.dongzi/persion/1234
                // 进行解析,返回值为10
                long id = ContentUris.parseId(uri);
                String where = "id=" + id;// 获取指定id的记录
                where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它条件附加上
                cursor=db.query(DBHelper.DB_NAME, projection, where, selectionArgs, null, null, sortOrder);
                break;
            default:break;
        }
        return cursor;
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        //该方法用于供外部应用更新ContentProvider中的数据。
        return 0;
    }

}

如果我们基本了解了上述说的基础知识,那么这些代码不难看懂,其实也非常简单,不直接操作DBHelper类,而是通过ContentProvider间接操作,而操作ContentProvider又是太通过
ContentResolver这个类,实现不同应用都可以访问这些数据。

本文章介绍了一篇关于手机开发中的android开时的 ui高效做法,有需要了解的朋友可以参考一下下哦。

一、选择恰当的图像尺寸

视图背景图总是会填充整个视图区域,图像尺寸的不适合会导致图像的自动缩放,为了避免这种情况,我们可以先将图片进行缩放到视图的大小。

 代码如下 复制代码

originalImage = Bitmap.createScaledBitmap(
originalImage, //被缩放图
view.getWidth(), //视图宽度
view.getHright(), //视图高度
true //双限行过滤器
);

二、去掉不需要的默认窗口背景

在默认情况下,窗口有一个不透明的背景,有时候我们并不需要他,就可以去掉他。因为更新看不见的窗口是浪费时间的。

去掉的方法:

1.代码实现:

 代码如下 复制代码

@Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //删除窗口背景
        getWindow().setBackgroundDrawable(null);
    }

2.xml里实现:

首先去顶你的res/xml/styles.xml里有

 代码如下 复制代码

<resources>
  <style name="NoBackGroundTheme" parent="android:Theme">
       <item name="android:windowBackground">@null</item>
</style>
</resources>
然后在你的manifest.xml里声明

<activity android:name="MyActivity" android:theme="@style/NoBackGroundTheme">
 ......
</activity>

三、尽可能的使用简单的布局和视图

如果一个窗口包含很多的视图,那么启动时间长、测量时间长、绘制时间长、布局时间长;

如果视图树深度太深,会导致StackOverflowException异常,和用户界面反映会很慢很慢。
解决的方法:

1.使用TextView的复合drawables,减少层次

如有这样的布局:

 代码如下 复制代码

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="@string/hello" />
    <Image android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:id="@+id/image" android:background="@drawable/icon" />
</LinearLayout>


我们可以这样来取代他,从而来将少层次:

 代码如下 复制代码

<TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="@string/hello" android:drawableRight="@drawable/icon"/>

2.使用ViewStub延迟展开视图

默认情况下,使用ViewStub包含的视图是不可见的。

 代码如下 复制代码

<ViewStub android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/vs" android:layout="@layout/main"/>

这个里面包含的main视图是不会展现出来的,如果需要展现出来需要代码的处理

 代码如下 复制代码

findViewById(R.id.vs).setVisibility(View.VISIBLE);

findViewById(R.id.vs).inflate();

3.使用<merge>合并视图
默认情况下,布局文件的根作为一个借点加入到父视图中,如果使用<merge>可以避免根节点。

如果最外层的布局是FrameLayout,那么可以使用merge替换掉,引用官方说明:

Obviously, using <merge /> works in this case because the parent of an activity's content view is always a FrameLayout. You could not apply this trick if your layout was using a LinearLayout as its root tag for instance.

 代码如下 复制代码

<merge
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
    .....
</merge>


4.使用RelativeLayout减少层次

5.自定义布局

Windows操作系统下,今天进行Android4.0安装时需要更新软件包,下载下来后自动安装出现这样一个问题:

Android2.2安装时需要更新软件包 - Elly - Elly?·

  解决办法:

  1.进入sdk的temp文件夹,下载好后会有一个tools_r15-windows.zip(版本号可能会不一样)

  2.解压此文件,如解压成tools_r15-windows

  3.进入tools_r15-windows文件夹,将tools_r15-windows里的文件复制

  4.覆盖sdk根目录的tools文件夹中的文件

本文章来解决在android2.3.4没有google map的真机上增加google map,有需要的同学可以参考一下。

 

使用Google Map时需依赖以下:
system/app
Location.apk(2.3.4版本不需要这个了)
NetworkLocation.apk
Maps.apk
Street.apk(2.3.4版本不需要这个了)
system/etc/permissions
com.google.android.maps.xml
system/framework
com.google.android.maps.jar

1、通过工具获得root权限;
2、adb shell    su回车 mount -o remount,rw /dev/block/mtdblock3 /system回车
3、adb -d push com.google.android.maps.jar /system/framework/ 或其它手机助手或豌豆夹等工具上传文件
   同时还有文件/system/etc/permissions/com.google.android.maps.xml
  NetworkLocation.apk放到system/app
4、把真机中/etc/permissions/platform.xml文件取出来编辑:<permissions>
<permissions>

本文章介绍一篇关于在手机开发中会出现的一些问题及全局异常处理的处理实现,有需要的朋友可以参考一下本文章。
 代码如下 复制代码

 

package org.wp.activity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Properties;
import java.util.TreeSet;

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.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 *
 *
 * UncaughtExceptionHandler:线程未捕获异常控制器是用来处理未捕获异常的。
 *                           如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
 *                           实现该接口并注册为程序中的默认未捕获异常处理
 *                           这样当未捕获异常发生时,就可以做些异常处理操作
 *                           例如:收集异常信息,发送错误报告 等。
 *
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 */
public class CrashHandler implements UncaughtExceptionHandler {
 /** Debug Log Tag */
 public static final String TAG = "CrashHandler";
 /** 是否开启日志输出, 在Debug状态下开启, 在Release状态下关闭以提升程序性能 */
 public static final boolean DEBUG = true;
 /** CrashHandler实例 */
 private static CrashHandler INSTANCE;
 /** 程序的Context对象 */
 private Context mContext;
 /** 系统默认的UncaughtException处理类 */
 private Thread.UncaughtExceptionHandler mDefaultHandler;
 
 /** 使用Properties来保存设备的信息和错误堆栈信息 */
 private Properties mDeviceCrashInfo = new Properties();
 private static final String VERSION_NAME = "versionName";
 private static final String VERSION_CODE = "versionCode";
 private static final String STACK_TRACE = "STACK_TRACE";
 /** 错误报告文件的扩展名 */
 private static final String CRASH_REPORTER_EXTENSION = ".cr";
 
 /** 保证只有一个CrashHandler实例 */
 private CrashHandler() {
 }

 /** 获取CrashHandler实例 ,单例模式 */
 public static CrashHandler getInstance() {
  if (INSTANCE == null)
   INSTANCE = new CrashHandler();
  return INSTANCE;
 }
 
 /**
  * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
  *
  * @param ctx
  */
 public void init(Context ctx) {
  mContext = ctx;
  mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
  Thread.setDefaultUncaughtExceptionHandler(this);
 }
 
 /**
  * 当UncaughtException发生时会转入该函数来处理
  */
 @Override
 public void uncaughtException(Thread thread, Throwable ex) {
  if (!handleException(ex) && mDefaultHandler != null) {
   // 如果用户没有处理则让系统默认的异常处理器来处理
   mDefaultHandler.uncaughtException(thread, ex);
  } else {
   // Sleep一会后结束程序
   // 来让线程停止一会是为了显示Toast信息给用户,然后Kill程序
   try {
    Thread.sleep(3000);
   } catch (InterruptedException e) {
    Log.e(TAG, "Error : ", e);
   }
   android.os.Process.killProcess(android.os.Process.myPid());
   System.exit(10);
  }
 }

 /**
  * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
  *
  * @param ex
  * @return true:如果处理了该异常信息;否则返回false
  */
 private boolean handleException(Throwable ex) {
  if (ex == null) {
   return true;
  }
  final String msg = ex.getLocalizedMessage();
  // 使用Toast来显示异常信息
  new Thread() {
   @Override
   public void run() {
    // Toast 显示需要出现在一个线程的消息队列中
    Looper.prepare();
    Toast.makeText(mContext, "程序出错啦:" + msg, Toast.LENGTH_LONG).show();
    Looper.loop();
   }
  }.start();
  // 收集设备信息
  collectCrashDeviceInfo(mContext);
  // 保存错误报告文件
  String crashFileName = saveCrashInfoToFile(ex);
  // 发送错误报告到服务器
  sendCrashReportsToServer(mContext);
  return true;
 }

 /**
  * 收集程序崩溃的设备信息
  *
  * @param ctx
  */
 public void collectCrashDeviceInfo(Context ctx) {
  try {
   // Class for retrieving various kinds of information related to the
   // application packages that are currently installed on the device.
   // You can find this class through getPackageManager().
   PackageManager pm = ctx.getPackageManager();
   // getPackageInfo(String packageName, int flags)
   // Retrieve overall information about an application package that is installed on the system.
   // public static final int GET_ACTIVITIES
   // Since: API Level 1 PackageInfo flag: return information about activities in the package in activities.
   PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
   if (pi != null) {
    // public String versionName The version name of this package,
    // as specified by the <manifest> tag's versionName attribute.
    mDeviceCrashInfo.put(VERSION_NAME, pi.versionName == null ? "not set" : pi.versionName);
    // public int versionCode The version number of this package,
    // as specified by the <manifest> tag's versionCode attribute.
    mDeviceCrashInfo.put(VERSION_CODE, pi.versionCode);
   }
  } catch (NameNotFoundException e) {
   Log.e(TAG, "Error while collect package info", e);
  }
  // 使用反射来收集设备信息.在Build类中包含各种设备信息,
  // 例如: 系统版本号,设备生产商 等帮助调试程序的有用信息
  // 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段
  Field[] fields = Build.class.getDeclaredFields();
  for (Field field : fields) {
   try {
    // setAccessible(boolean flag)
    // 将此对象的 accessible 标志设置为指示的布尔值。
    // 通过设置Accessible属性为true,才能对私有变量进行访问,不然会得到一个IllegalAccessException的异常
    field.setAccessible(true);
    mDeviceCrashInfo.put(field.getName(), field.get(null));
    if (DEBUG) {
     Log.d(TAG, field.getName() + " : " + field.get(null));
    }
   } catch (Exception e) {
    Log.e(TAG, "Error while collect crash info", e);
   }
  }
 }
 
 /**
  * 保存错误信息到文件中
  *
  * @param ex
  * @return
  */
 private String saveCrashInfoToFile(Throwable ex) {
  Writer info = new StringWriter();
  PrintWriter printWriter = new PrintWriter(info);
  // printStackTrace(PrintWriter s)
  // 将此 throwable 及其追踪输出到指定的 PrintWriter
  ex.printStackTrace(printWriter);

  // getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。
  Throwable cause = ex.getCause();
  while (cause != null) {
   cause.printStackTrace(printWriter);
   cause = cause.getCause();
  }

  // toString() 以字符串的形式返回该缓冲区的当前值。
  String result = info.toString();
  printWriter.close();
  mDeviceCrashInfo.put(STACK_TRACE, result);

  try {
   long timestamp = System.currentTimeMillis();
   String fileName = "crash-" + timestamp + CRASH_REPORTER_EXTENSION;
   // 保存文件
   FileOutputStream trace = mContext.openFileOutput(fileName, Context.MODE_PRIVATE);
   mDeviceCrashInfo.store(trace, "");
   trace.flush();
   trace.close();
   return fileName;
  } catch (Exception e) {
   Log.e(TAG, "an error occured while writing report file...", e);
  }
  return null;
 }
 
 /**
  * 把错误报告发送给服务器,包含新产生的和以前没发送的.
  *
  * @param ctx
  */
 private void sendCrashReportsToServer(Context ctx) {
  String[] crFiles = getCrashReportFiles(ctx);
  if (crFiles != null && crFiles.length > 0) {
   TreeSet<String> sortedFiles = new TreeSet<String>();
   sortedFiles.addAll(Arrays.asList(crFiles));

   for (String fileName : sortedFiles) {
    File cr = new File(ctx.getFilesDir(), fileName);
    postReport(cr);
    cr.delete();// 删除已发送的报告
   }
  }
 }

 /**
  * 获取错误报告文件名
  *
  * @param ctx
  * @return
  */
 private String[] getCrashReportFiles(Context ctx) {
  File filesDir = ctx.getFilesDir();
  // 实现FilenameFilter接口的类实例可用于过滤器文件名
  FilenameFilter filter = new FilenameFilter() {
   // accept(File dir, String name)
   // 测试指定文件是否应该包含在某一文件列表中。
   public boolean accept(File dir, String name) {
    return name.endsWith(CRASH_REPORTER_EXTENSION);
   }
  };
  // list(FilenameFilter filter)
  // 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录
  return filesDir.list(filter);
 }

 private void postReport(File file) {
  // TODO 使用HTTP Post 发送错误报告到服务器
  // 这里不再详述,开发者可以根据OPhoneSDN上的其他网络操作
  // 教程来提交错误报告
 }

 /**
  * 在程序启动时候, 可以调用该函数来发送以前没有发送的报告
  */
 public void sendPreviousReportsToServer() {
  sendCrashReportsToServer(mContext);
 }
}

[!--infotagslink--]

相关文章

  • php抓取网站图片并保存的实现方法

    php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • mysql 批量更新与批量更新多条记录的不同值实现方法

    批量更新mysql更新语句很简单,更新一条数据的某个字段,一般这样写:复制代码 代码如下:UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value';如果更新同一字段为同一个值,mysql也很简单,修改下where即...2013-10-04
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • EXCEL数据上传到SQL SERVER中的简单实现方法

    EXCEL数据上传到SQL SERVER中的方法需要注意到三点!注意点一:要把EXCEL数据上传到SQL SERVER中必须提前把EXCEL传到服务器上.做法: 在ASP.NET环境中,添加一个FileUpload上传控件后台代码的E.X: 复制代码 代码如下: if...2013-09-23
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • Android中使用SDcard进行文件的读取方法

    首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
  • Android开发之PhoneGap打包及错误解决办法

    下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
  • 用Intel HAXM给Android模拟器Emulator加速

    Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20
  • Android判断当前屏幕是全屏还是非全屏

    在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20