[mobilesafe] 09_备份短信-回调函数

Android 4.0

备份短信

义务方法里面有异常,且没有返回值,是一个void,那么异常必须抛出去。
一、备份短信
步骤:
1、利用短信的内容提供者来获取短信的内容,短信的内容提供者,定义如下
\packages\providers\TelephonyProvider\AndroidManifest.xml
<provider android:name="SmsProvider"
          android:authorities="sms"
          android:multiprocess="true"
          android:exported="true"
          android:readPermission="android.permission.READ_SMS"
          android:writePermission="android.permission.WRITE_SMS" />
2、查看系统源码的uri定义
private static final int SMS_ALL = 0;
static {
   sURLMatcher.addURI("sms", null, SMS_ALL);
   ......
}
3、查看短信数据库\data\data\com.android.providers.telephony\databases\mmssms.db
查看sms表中的内容:
address(短信地址),
date(发送接收日期long型),
type(类型,接收或者发送),1表示接收到的短信,2表示发送出去的短信
body(短信的内容)

4、权限:
<uses-permission android:name="android.permission.READ_SMS"/>

5、备份短信是一个耗时的操作,应该将其放在子线程里面进行

6、ProgressDialog进度对话框的使用
// 需求1、进度对话框
final ProgressDialog progressDialog = new ProgressDialog(this);
// a) 设置进度对话框的参数
progressDialog.setTitle("提醒:");
progressDialog.setMessage("正在备份短信~~~");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// b)显示进度对话框,一定要
progressDialog.show();
//由于没有开启子线程,所以要等备份完毕后,才能显示出来进度对话框完成,且备份过程很慢,不应该放在主线程里面,应该放在子线程里面
progressDialog.setMax(total);
progressDialog.setProgress(progress);
progressDialog.dismiss();

7、ProgressBar进度条

8、XmlSerializer备份短信到xml文件

9、回调函数
A:写界面的
B:写工具类的
如果一下需求
需求1:进度对话框
需求2:进度条
那此时,B需要不断的修改他所写的工具类
解决:B可以定义接口,在其接口中定义要调用的方法,并提供一个接口出去,他的代码里面只调用接口中的方法,如果A实现了接口中的方法,即使A中变化了,B也不需要改变其源程序。

核心代码:
AdvancedToolsActivity.java
package cn.zengfansheng.mobilesafe;
import java.io.IOException;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.ProgressBar;
import cn.zengfansheng.mobilesafe.utils.SmsUtils;
import cn.zengfansheng.mobilesafe.utils.SmsUtils.BackupCallback;
import cn.zengfansheng.mobilesafe.utils.ToastUtils;
/**
 * 11、高级工具Activity
 * 
 * @author hacket
 * 
 */
public class AdvancedToolsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_advanced_tools);
    }
    /**
     * 1、查询手机号码、固话归属地-离线查询
     * 
     * @param view
     */
    public void queryNumberAddress(View view) {
        Intent intent = new Intent(this, NumberQueryActivity.class);
        startActivity(intent);
    }
    
    /**
     * 2、常见热线号码查询
     * 
     * @param view
     */
    public void queryCommonNumber(View view){
        Intent intent = new Intent(this, CommnumNumberActivity.class);
        startActivity(intent);
    }
    
    /**
     * 3、短信备份 
     * @param view
     */
    public void backupSms(View view){
        
        String externalStorageState = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(externalStorageState)) {
            // 需求1、进度对话框
            final ProgressDialog progressDialog = new ProgressDialog(this);
            // a) 设置进度对话框的参数
            progressDialog.setTitle("提醒:");
            progressDialog.setMessage("正在备份短信~~~");
            progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            // b)显示进度对话框,一定要
            progressDialog.show();
            // 由于没有开启子线程,所以要等备份完毕后,才能显示出来进度对话框完成,且备份过程很慢,不应该放在主线程里面,应该放在子线程里面
            // 需求2、进度条
            final ProgressBar progressBar = (ProgressBar) this.findViewById(R.id.pb_backup_sms);
            progressBar.setVisibility(ProgressBar.VISIBLE);// 进度条可见
            new Thread() {
                public void run() {
                    try {
                        
                        String path = Environment.getExternalStorageDirectory()+"/smsbackup.xml";
                        
//                        SmsUtils.backupSms(AdvancedToolsActivity.this, path, progressDialog); //需求1:进度对话框
//                        SmsUtils.backupSms(AdvancedToolsActivity.this, path,pb_backup);//需求2:进度条
                        
                        // 回调函数,这里实现,定义回调的地方调用
                        SmsUtils.backupSms(AdvancedToolsActivity.this, path, new BackupCallback() {
                            
                            @Override
                            public void beforeBackup(int total) {
                                //需求1:进度对话框
                                progressDialog.setMax(total);
                                //需求2: 进度条    
                                progressBar.setMax(total);
                            }
                            @Override
                            public void onBackupProcess(int progress) {
                                //需求1:进度对话框
                                progressDialog.setProgress(progress);
                                //需求2: 进度条    
                                progressBar.setProgress(progress);
                            }
                            
                        });
                        
                        ToastUtils.showToastInThread(AdvancedToolsActivity.this"备份短信成功~~~" + path);
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                        ToastUtils.showToastInThread(AdvancedToolsActivity.this"备份短信失败~~~");
                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                        ToastUtils.showToastInThread(AdvancedToolsActivity.this"备份短信失败~~~");
                    } catch (IOException e) {
                        e.printStackTrace();
                        ToastUtils.showToastInThread(AdvancedToolsActivity.this"备份短信失败~~~");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        ToastUtils.showToastInThread(AdvancedToolsActivity.this"备份短信失败~~~");
                    } finally {
                        // 1、将进度对话框dismiss
                        progressDialog.dismiss();
                        // 2、进度条不可见 FIXME
                        // progressBar.setVisibility(ProgressBar.INVISIBLE);
                        //Only the original thread that created a view hierarchy can touch its views.
                    }
                };
            }.start();
        }
    }
    
    /**
     * 4、短信恢复
     * @param view
     */
    public void restoreSms(View view) {
        
    }
}
SmsUtils.java  回调函数
package cn.zengfansheng.mobilesafe.utils;
 
import java.io.FileOutputStream;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
import android.util.Xml;
 
/**
* 4、短信工具类
* @author hacket
*/

public class SmsUtils {
 
    /**
     * 定义接口
     * @author hacket
     */

    public interface BackupCallback {
        /**
         * 在备份短信之前调用的方法,用来设置总的短信备份数
         * @param total 一共多少条短信需要备份
         */

        public abstract void beforeBackup(int total);
        /**
         * 在备份短信过程中调用的方法
         * @param progress 当前已经备份了多少条短信
         */

        public abstract void onBackupProcess(int progress);
    }
 
    private static final String TAG = "SmsUtils";
 
    /**
     * 1、短信备份
     * @param context 上下文
     * @param path 备份短信的路径地址
     * @param  callback 定义一个回调的接口
     * @throws IllegalArgumentException
     * @throws IllegalStateException
     * @throws IOException
     * @throws InterruptedException
     */

    public static void backupSms(Context context, String path, BackupCallback callback)
            throws IllegalArgumentException, IllegalStateException, IOException, InterruptedException {
 
        /*<provider android:name="SmsProvider"
                  android:authorities="sms"
                  android:multiprocess="true"
                  android:exported="true"
                  android:readPermission="android.permission.READ_SMS"
                  android:writePermission="android.permission.WRITE_SMS" />*/

 
        // a、获取内容提供者
        ContentResolver contentResolver = context.getContentResolver();
 
        // b、查询短信
        Uri uri = Uri.parse("content://sms");// content固定,android:authorities="sms"
        Cursor cursor = contentResolver.query(uri, new String[] { "address", "date", "type","body" }, null, null, null);
 
        // 1、xml序列化器
        XmlSerializer xmlSerializer = Xml.newSerializer();
        FileOutputStream fos = null;
 
        // 2、设置序列化参数
        fos = new FileOutputStream(path);
        xmlSerializer.setOutput(fos, "utf-8");
        xmlSerializer.startDocument("utf-8", true);
 
        // 3、读取一条短信就备份
        int total = cursor.getCount();// 总的短信条数
        Log.i(TAG, "总的短信条数:" + total);
 
        // progressDialog.setMax(total);//需求1:进度对话框
        // pb.setMax(total);// 需求2:进度条
        callback.beforeBackup(total);// 使用接口,以后需求变化,就可以不用修改程序
 
        int progress = 0;
        while (cursor.moveToNext()) {
 
            xmlSerializer.startTag(null, "smss");
 
            xmlSerializer.startTag(null, "sms");
            xmlSerializer.text(cursor.getString(0));
            xmlSerializer.endTag(null, "sms");
 
            xmlSerializer.startTag(null, "date");
            xmlSerializer.text(cursor.getString(1));
            xmlSerializer.endTag(null, "date");
 
            xmlSerializer.startTag(null, "type");
            xmlSerializer.text(cursor.getString(2));
            xmlSerializer.endTag(null, "type");
 
            xmlSerializer.startTag(null, "sms");
            xmlSerializer.text(cursor.getString(3));
            xmlSerializer.endTag(null, "sms");
 
            xmlSerializer.startTag(null, "body");
            xmlSerializer.text(cursor.getString(0));
            xmlSerializer.endTag(null, "body");
 
            xmlSerializer.endTag(null, "smss");
            fos.flush();
            Thread.sleep(500);
            progress++;
 
            // progressDialog.setProgress(progress);//需求1:进度对话框
            // pb.setProgress(progress);// 需求2:进度条
            callback.onBackupProcess(progress);// 使用接口,以后需求变化,就可以不用修改程序
        }
        xmlSerializer.endDocument();
        // 4、关闭资源
        cursor.close();
        fos.close();
    }
}


问题1:异常:
11-07 16:01:57.159: E/AndroidRuntime(31528): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
问题2:
对于备份的短信,如果使用明文备份,不安全,应该使用密文加密。
简单的加密算法:
加密:定义一个密码,然后将该密码与源数据进行异或操作,得到一个密文
解密:再用这个密码和密文进行一次异或,又重新得明文
更深层次:
可以将高四位和低四位进行交换。